一次 Android 线上 OOM 的排查过程
2024-01-27 10:18:15
一天,后台统计到线上有大量 OOM 崩溃,小王收到老板的紧急指令,立即排查!小王心想,这还不简单,待我看看崩溃堆栈,分分钟解决。于是小王不慌不忙的打开崩溃后台,一看傻眼了…
令人头疼的崩溃堆栈
映入小王眼帘的是一串晦涩难懂的崩溃堆栈,没有任何有价值的信息。小王挠了挠头,心想,这可咋整?这堆栈压根看不出问题啊!
无奈之下,小王只好硬着头皮开始分析。他仔细查看了崩溃堆栈中的每一行,试图找出蛛丝马迹。终于,他在崩溃堆栈的最后几行发现了一条线索:
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:201)
这条堆栈信息表明,OOM 崩溃是由垃圾回收器中的“终止器守护线程”(FinalizerDaemon)引发的。这给了小王一个新的思路,他决定从垃圾回收的角度入手进行排查。
MAT 工具助攻
小王打开 MAT(Memory Analyzer Tool)工具,导入线上崩溃时的堆转储文件。经过一番分析,小王发现了一个惊人的事实:堆中存在大量的未引用的 Bitmap 对象。
Bitmap 是 Android 中用来处理图像的类。在 Android 开发中,Bitmap 对象需要手动释放,否则就会导致内存泄漏。小王推测,线上 OOM 崩溃的原因就是由于未释放的 Bitmap 对象过多导致的。
代码审查
为了进一步确认自己的推测,小王对线上代码进行了仔细审查。经过一番搜索,他发现了一个潜在的问题点:
private Bitmap mBitmap;
public void onCreate(Bundle savedInstanceState) {
...
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.my_image);
...
}
public void onDestroy() {
...
// 未释放 Bitmap 对象
}
在这个代码片段中,小王在 onCreate() 方法中创建了 Bitmap 对象,但在 onDestroy() 方法中却忘记释放它了。这会导致 Bitmap 对象无法被垃圾回收器及时回收,从而造成内存泄漏。
解决方案
为了解决 OOM 崩溃问题,小王对代码进行了修改,在 onDestroy() 方法中添加了释放 Bitmap 对象的代码:
public void onDestroy() {
...
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
}
经过修改后,小王重新编译并部署了代码。经过一段时间的观察,线上 OOM 崩溃的数量大幅下降。至此,这场 OOM 排查之战终于告一段落。
总结
通过这次 OOM 排查过程,小王总结出了以下经验教训:
- 重视崩溃堆栈分析: 崩溃堆栈是 OOM 排查的重要线索,切勿忽视。
- 善用 MAT 工具: MAT 工具可以帮助分析堆转储文件,找出内存泄漏的根源。
- 仔细审查代码: OOM 崩溃往往是由代码中的内存泄漏造成的,因此仔细审查代码至关重要。
- 及时释放资源: Android 中的 Bitmap 等资源需要手动释放,否则会导致内存泄漏。
- 持续监控线上稳定性: 及时发现和解决线上问题,是保证服务稳定性的关键。