Fork me on GitHub

Android Framework 专项 - Handler(三)

要理解 Message 的创建,先要说内存抖动

内存抖动的原因及引发的问题

在 Android 虚拟机中,当我们在应用程序中创建大量的临时对象,并且这些对象的存在时间很短暂,虚拟机会识别这些临时对象并将它们标记为待清理的垃圾对象,以便在适当的时候回收它们。然而,这个垃圾回收过程可能会引发一个称为“stop the world”的现象。所谓“stop the world”指的是,在进行垃圾回收的过程中,虚拟机会暂停应用程序的正常执行,以便能够安全地识别和回收这些临时对象。

在 Java 字节码中,创建一个对象通常需要执行三个关键操作: newdupinvokespecial。这三个操作合在一起构成了对象创建的原子过程。

虚拟机通常通过两种方式来判断一个对象是否是垃圾。一种方式是使用“引用计数”机制,但这种方法存在循环引用的问题,因此无法完全准确地标记垃圾对象,从而导致垃圾回收不彻底。另一种方式是使用“根可达”(或称为“根引用”)的方式,即从根对象(如线程栈、静态变量等)开始,追踪对象之间的引用关系,从而判断哪些对象是可达的,哪些是垃圾。

然而,在标记垃圾对象的过程中,如果垃圾回收线程与创建对象的线程同时工作,就可能出现问题。例如,如果一个对象刚刚被创建但尚未与变量关联,而垃圾回收线程已经扫描到这个对象并标记为垃圾,那么在后续的垃圾回收过程中,这个对象可能会被回收,而在应用程序中使用相关变量时可能会触发空指针异常。为了避免这种情况,标记垃圾的过程通常需要暂停所有工作线程,确保在标记过程中不会有对象的状态发生变化。这就是为什么在进行垃圾回收时会出现应用程序“停顿”的现象。

然而,如果垃圾回收过程耗时过长,会导致应用程序长时间无响应,造成卡顿现象。因此,我们需要尽量避免在应用中创建大量生命周期很短的临时对象,以减少垃圾回收的频率和影响。

内存抖动的原因: 创建了大量的生命周期很短的对象。
导致的问题: 在用户看来程序出现了卡顿。

Message 中 obtain() 与 recycle()

显而易见,Message 那真是生命周期极短数量极其庞大,是最容易内存抖动的地方
所以 Message 内存必须被复用

我们说说 Message 的销毁与创建

创建: android.os.Message#obtain()

注意:说创建是不准确的,应该是提供

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

这段代码是用于在 Android 中创建和管理消息对象的一部分。它通过维护一个对象池,允许在需要时重复利用已有的消息对象,从而减少内存分配和垃圾回收的开销。当需要获取一个新的消息对象时,它会首先检查对象池是否有可用的对象。如果有,它会从对象池中取出一个对象并重置其状态,然后返回;如果对象池为空,它会创建一个新的消息对象并返回。这种做法有助于提高应用程序的性能和效率。

1
2
3
4
5
synchronized (sPoolSync) {
if (sPool != null) {
...
}
}

pFOuBmd.png

1
Message m = sPool;

pFOuD0A.png

1
sPool = m.next;

pFOuwOH.png

1
m.next = null;

pFOu6tP.png

1
2
3
m.flags=0;
sPoolSize--;
return m;
回收: android.os.Message#recycle()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}

这里提一下 gCheckRecycle

1
2
3
4
5
public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
gCheckRecycle = false;
}
}

在较低版本的 Android 上 ( < 5.0 ),某些检查可能会导致不必要的异常情况,如 IllegalStateException。这可能会影响应用程序的稳定性。通过避免这些检查,可以避免异常的抛出,提高应用程序的可靠性。

回收: android.os.Message#recycleUnchecked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
@UnsupportedAppUsage
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;

消息的标志位 flags 的含义是消息对象是否正在使用中。

1
2
3
4
5
6
7
8
9
@UnsupportedAppUsage
void recycleUnchecked() {
...
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
...
}
}
}

在同步块中,检查消息池的大小是否小于最大池大小(MAX_POOL_SIZE)。

  • 如果是,将当前消息对象放入消息池的链表中,以供将来重用。
  • 更新消息池的大小。

pFOud6e.png

1
next=sPool;

pFOurTI.png

1
sPool=this;

pFOuykt.png


内存抖动参考自:
知乎博主Android小瓜: Android 性能优化大法——内存抖动
原文链接: https://zhuanlan.zhihu.com/p/575959909

android.os.Message#obtain() 与 android.os.Message#recycleUnchecked 相关图片来自于:
版权声明:本文为CSDN博主「-_-void」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xmh19936688/article/details/51901338

,