两个OOM Cases排查过程的分享

分享一下两个OOM Cases的查找过程,一个应用是Native OOM;另外一个应用其实没有OOM,只是每隔一段时间就会出现频繁FGC的现象,OOM的查找已经具备了不错的工具,但有些时候还是会出现很难查的现象,希望这两个排查过程的分享能给需要的同学带来一些帮助。

Native OOM的排查Case
之前的几个PPT里我都说到了,目前查找Native OOM最好的方法就是用google perftools了,于是挂上google perftools,等待应用再次native oom,很幸运,两天后,应用就再次native oom了,于是分析crash之前那段时间谁在不断的分配堆外的内存,pprof看到的结果主要是java.util.Inflater造成的,由于之前已经碰到过类似的case,知道如果使用了Inflater,但不显式的调用Inflater.end的话,确实会造成这个现象。
于是剩下的问题就是找出代码里什么地方调用了Inflater,这种时候btrace这个神器就可以发挥作用了,一个简单的btrace脚本:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace public class Trace{
@OnMethod(
clazz="java.util.zip.Inflater",
method="/.*/"
)
public static void traceExecute(@ProbeMethodName String methodName){
println(concat("who call Inflater.",methodName));
jstack();
}
}
执行后很快就找到了代码什么地方调用了Inflater,于是加上了显式的调用Inflater.end,搞定收工。

偶尔频繁FGC的排查Case
这个Case就没上面的那个那么顺利了,查了有接近一个月才查出最终的原因。
当这个应用出现频繁FGC时,dump了内存,用MAT分析后,看到内存被消耗完的原因是由于几个线程内的ThreadLocalMap中有大量的数据,ThreadLocal中消耗最多内存的主要是一个HashMap,这里面有大量的数据。
于是当时想到的第一个方法就是查查应用里面什么地方往ThreadLocal里放了HashMap,杯具的是,当查找代码后发现应用本身的代码并没有往 ThreadLocal里放HashMap,那就只能是应用依赖的其他jar包做了这样的事了,但不可能去抓出这个应用依赖的所有的jar的源码来扫描,于是继续借助BTrace,写了个脚本来跟踪这类型的线程中谁调用了ThreadLocal.set,并且放的是HashMap,btrace脚本如下:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;

@BTrace public class Trace{
@OnMethod(
clazz="java.lang.ThreadLocal",
method="set"
)
public static void traceExecute(Object value){
if(startsWith(name(currentThread()),"xxx") && startsWith("java.util.HashMap",name(classOf(value))) ){
println("-------------------------");
jstack();
println();
}
}
}
OK,开始运行上面的脚本,发现竟然一直都没打印出什么内容,只能一直等了,杯具的是一直到了一周后再次出现频繁FGC时,这个脚本都没输出任何的东西,于是只好转换思路。

既然是HashMap里put了大量的某种类型的数据,那干脆用btrace来看看是谁在往HashMap里put这些数据,于是又写了一个 btrace脚本,执行后,很快就看到了是代码中什么地方在put这些数据,但是从抓到的调用者来看,不仅仅是目前有大量数据的这类型的线程会调,其他类型的线程也会调用,如果这个地方有问题的话,应该就全部有问题了,于是跳过这里。

回到MAT看到的现象,会不会是因为代码什么地方用ThreadLocal的方式不对,又或是什么地方往ThreadLocal里放了东西,又忘了清除呢,因此要做的就是找出这个应用中所有属性为ThreadLocal的地方,来人肉分析了,于是写了一个jsp,扫描所有的classloader中的所有class,找出属性类型为ThreadLocal的,扫描后找到了一些,还真发现有一个和现在HashMap中放的数据一样的private ThreadLocal,这种用法在线程复用的情况下,如果是每次new ThreadLocal的话,会导致ThreadLocal放的东西一直不释放,兴奋的以为已经发现原因了,可惜和业务方一确认,这个类借助Spring 保证了singleton的,因此不会有问题。
好吧,到这一步,只能猜想是由于某种参数请求的时候造成业务上会获得大量的数据了,于是想着要找业务方来分析代码了,这个非常麻烦,于是到此就几乎停滞不前了。

今天静下心来,重新仔细的看了下MAT分析的结果,决定仍然用btrace跟踪下之前往HashMap中put数据的那个业务代码,突然发现,在 web类型的处理线程中它借助的是filter去clear数据的,而杯具的是出问题的这种类型线程的处理机制是没有filter机制的,因此猜测问题估计出在这里了,继续btrace,看看这种类型的线程中是不是只有人调put,没人调clear,btrace脚本运行,很快就验证了这个猜测,于是相应的解决掉了这个case,搞定收工。

在这第二个case中,可见在频繁FGC或者OOM时,很有可能MAT只能告诉你初步的原因,但要对应到代码上到底是什么地方造成的,还得花很大精力分析了,这个时候BTrace通常能帮上很大的忙。
# Android 死机重启问题常见分类与原因总结 ## 1. 硬件问题 - **屏幕异常** - 屏不良:表现为黑屏(如屏幕排线损坏、显示驱动故障)。 - TPL问题:表现为定屏(触摸面板逻辑异常)。 - **存储/DDR故障** - 内存颗粒损坏、存储芯片虚焊导致读写异常,引发系统崩溃。 - **电源与电池问题** - 电压不稳、电池老化导致供电不足,触发强制重启。 --- ## 2. 系统层问题 ### 底层系统(内核/驱动) - **Kernel Panic** - 内核驱动异常(如 GPU 驱动崩溃、文件系统损坏)。 - **Watchdog 超时** - `system_server_watchdog`:系统关键服务(如 ActivityManager)未响应。 - **进程 D 状态(不可中断睡眠)** - I/O 阻塞或驱动未释放资源,导致进程卡死,最终触发重启。 ### 上层系统服务 - **WMS(窗口管理服务)异常** - 窗口渲染逻辑错误,导致界面冻结。 - **SurfaceFlinger 卡住** - 图形合成服务阻塞,表现为定屏或黑屏。 - **锁屏服务异常** - 指纹/人脸识别模块阻塞亮屏逻辑,导致黑屏(如 `occlude` 状态异常)。 --- ## 3. 应用层问题 - **应用 ANR(无响应)** - 主线程阻塞(如死锁、复杂计算),导致界面定屏,用户感知为系统卡死。 - **内存泄漏/OOM** - 应用持续占用内存,触发系统 `Low Memory Killer` 强制终止进程或重启。 - **多线程冲突** - 资源竞争引发死锁,导致进程挂起。 --- ## 4. 图形显示类问题 - **图形库异常(如 OpenGL/Vulkan)** - 游戏/视频渲染时驱动崩溃,导致黑屏或定屏。 - **Surface 管理失败** - 多图层叠加冲突,引发显示服务(SurfaceFlinger)崩溃。 --- ## 5. 外部因素与兼容性问题 - **过热保护** - CPU/GPU 温度过高触发温控降频或强制重启。 - **OTA 升级失败** - 系统分区校验错误,导致启动循环(Bootloop)。 - **第三方模块兼容性** - 非官方内核/Recovery 修改系统分区,引发不稳定。 --- ## 6. 文件系统与存储异常 - **Data 分区损坏** - 异常断电导致文件系统错误(如 `EXT4` 结构损坏)。 - **存储满/权限错误** - 系统关键目录(如 `/system`)写入失败,引发服务崩溃。 --- ## 典型现象与对应原因速查 | **现象** | **可能原因** | |-------------------------|------------------------------------------| | 黑屏且无响应 | 屏不良、SurfaceFlinger 崩溃、电池故障 | | 定屏但可触发重启 | 应用 ANR、进程 D 状态、WMS 异常 | | 频繁自动重启 | 内核 Panic、温控保护、Data 分区损坏 | | 亮屏黑屏(有状态栏) | 锁屏服务异常(如 Findx2 的 `occlude` 问题) |
最新发布
03-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值