问题:
主线程每5秒钟发一个执行10秒的消息到子线程,会发生什么?
回答:
主线程每5秒钟发一个执行10秒的消息到子线程,假设在整个过程中,不论是主线程的5秒定时发送,还是子线程10s执行的逻辑都能正常按预期得到CPU按时执行,那么对于子线程而言,每10秒可以在其消息队列中形成一个消息堆积,当时间足够长的时候,因为内存不足以构造新的Message对象,进而导致OOM异常。
解析
编写测试代码,每5秒向HandlerThread发送一条消息,该消息需执行10秒,为减少测试时间,发往HandlerThread的消息obj指定为大内存对象,代码如下:
public class MemoryTest extends AppCompatActivity {
private static final String TAG = "MemoryTest";
private static final int MSG_WHAT = 100;
private static final int MAIN_DELAY_MILLIS = 5000;
private static final int BG_DELAY_MILLIS = 10000;
private HandlerThread mHandlerThread;
private Handler mBgHandler;
private Handler mMainHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memory_test);
mHandlerThread = new HandlerThread("BG_HANDLER");
mHandlerThread.start();
mBgHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == MSG_WHAT) {
// 处理子线程消息,执行10s
SystemClock.sleep(BG_DELAY_MILLIS);
return;
}
super.handleMessage(msg);
}
};
mMainHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == MSG_WHAT) {
// 打印HandlerThread消息队列的排队情况
mHandlerThread.getLooper().dump(new Printer() {
@Override
public void println(String x) {
Log.d(TAG, x);
}
}, TAG);
Message message = new Message();
message.what = MSG_WHAT;
// message.obj是一个1M内存占用的大对象
message.obj = new byte[1024 * 1024];
// 向子线程发送消息
mBgHandler.sendMessage(message);
// 触发主线程消息消息循环
mMainHandler.sendEmptyMessageDelayed(MSG_WHAT, MAIN_DELAY_MILLIS);
return;
}
super.handleMessage(msg);
}
};
mMainHandler.sendEmptyMessage(MSG_WHAT);
}
}
在Genymotion模拟器上执行上述测试代码,当队列中堆积消息数量达到92个时,再次创建发送给子线程的Message失败,爆出OOM异常,日志打印如图所示:
综上,当我们使用主线程定时5秒向子线程发送一个执行10秒的消息时,等待时间足够长且不清理子线程消息队列的情况下,会发生OOM异常。