一次不讲武德的 Android 线上 OOM 的排查过程

作者:王晨彦

开篇

一天,后台统计到线上有大量 OOM 崩溃,小王收到老板的紧急指令,立即排查!

小王心想,这还不简单,待我看看崩溃堆栈,分分钟解决。

于是小王不慌不忙的打开崩溃后台,一看傻眼了,同样的 OOM,却有几十种不同的堆栈,大到创建 View,小到 new 一个 String。

小王差点骂了出来:这 OOM 不讲武德啊!

骂完之后,还是得解决问题啊,否则怎么面对老板啊。

心路历程

正郁闷着,小王突然想起曾经看过性能优化的文章,里面介绍了 Android Studio 中集成的 Profiler 可以分析 APP 内存。

既然堆栈看不出什么问题,那就只能照着文章的方法,碰碰运气了。

于是小王点开了 IDE 底部那个毫不起眼的「Profiler」面板,映入眼帘的是:

小王一眼就看到了 MEMORY 栏,这不就是内存使用嘛。

嗯,数据倒是挺全,可是怎么知道哪里导致 OOM 了啊,小王又开始怀疑人生了…

“放着不动肯定看不出什么啊,内存是动态申请的嘛。”

小王心想,既然这么多 OOM,那么肯定是 APP 内的常用页面导致的,于是小王开始一边来回切换常用页面,一边观察内存走势。

经过多次尝试,小王发现应用的内存占用确实在不断升高,即使手动 GC 之后,仍然居高不下。

小王想起面试宝典中「无法被 GC 回收的对象,会导致内存泄露」,于是手动点了下 GC,避免数据不准确。

Java 堆从 15.7MB 涨到 19.3MB,好像问题不大,而 Native 就离谱了,好家伙,竟然从 56.1MB 涨到了 97.5MB,分分钟就涨了 40MB+。

小王喜出望外,终于发现内存问题了!看来平时摸鱼的时候多看看文章真是没坏处啊。

可是,就算知道内存不正常,但还是不能定位是哪段代码导致了…

小王平复了一下心情,继续观察规律,终于发现,每次从A页面跳转出去,内存都会增加几M,而且 GC 无法回收,那肯定是这个页面有问题了!

于是小王骂骂咧咧的开始阅读这个页面的代码,希望能够发现内存泄露的元凶。心里嘀咕着,让我看看是哪个 ** 写出了内存泄露的代码。

小王逐字逐句看完了代码:可是并没有什么问题啊,就是一个普通的列表页,还是用 RecyclerView 实现的,没啥毛病啊。

这下又把小王难住了,小王心想,不能在黎明前倒下啊,于是又想起文章中关于 Profiler 的介绍,可以使用 Dump 功能方便的查看当前的内存快照,兴许能发现什么端倪呢。

好家伙,原来是 Bitmap 占了这么大内存,于是小王又想起面试宝典。

Android 2.3.3(API level 10) 和更早的版本,Bitmap 对象和对象里对应的像素数据是分开存储的,Bitmap 存在虚拟机的堆里,而像素数据存储在 Native 内存里。

从 Android 3.0(API level 11) 到 Android 7.1(API level 25),Bitmap 对象及其像素数据都存储在虚拟机的堆里。

从 Android 8.0(API level 26) 开始,Bitmap 对象存储在虚拟机的堆里,而对应的像素数据存储在 Native 堆里。

小王测试的手机是 Android 10,Bitmap 数据存储在 Native 堆,所以基本上可以确定就是 Bitmap 导致内存泄露了。虽然又前进了一大步,但还是找不到原因。

小王发现,点击对象,可以查看所有实例的引用链,这下可把小王高兴坏了,而且小王还发现了一个非常可疑的引用链。

这不是 Coil 的 Memory Cache 嘛,可是这里明明是有缓存的嘛,怎么还会泄露,难不成是这个开源库有 bug?

https://github.com/coil-kt/coil

小王怀着忐忑的心情打开了 RealMemoryCache 这个类。

这不就是一个基于 LRU 实现的内存缓存嘛,乍一看好像没什么毛病。

没时间仔细研究了,小王心想,先看看开源社区有没有人反馈过这个问题,小王过滤了一下包含 "memory leak" 关键字的 issue。

果然有一个 PR 的标题非常接近 Fix memory leak if request is started on detached view.

https://github.com/coil-kt/coil/pull/518

看起来问题已经被修复且已经发布了新版本,于是小王立马升级版本再次测试,果然没有泄露了。

于是立马提交代码,兴冲冲的去找老板炫耀了!!!

追根溯源

回过头来,小王心想,作为一个“有上进心”的程序员,我得看看是什么原因导致的泄露啊。

于是再次打开 PR,在诸多改动中,终于找到一个真正的代码改动,其他都是测试用例。

小王不禁感慨,歪果仁就是专业呀,改了两行代码就要写一堆测试用例。

小王终于弄清了导致泄露的原因,原来是在快速切换页面时,有时页面已经销毁了,才开始加载图片,此时 Coil 会把这个 View 对象保存起来,等待 View detach 的时候释放,然而此时 View 已经是 detach 的状态了,因此永远不会被释放了,而 Bitmap 又被 View 持有,而我们都知道 Bitmap 是内存占用大户,因此就出现了上面 Bitmap 占用大量内存的情况。

而解决方案就是再判断一下 View 是否已经 Detach,是的话就直接释放了,避免造成泄露。

最后小王高高兴兴的下班了。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,501评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,673评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,610评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,939评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,668评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,004评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,001评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,173评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,705评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,426评论 3 359
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,656评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,139评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,833评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,247评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,580评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,371评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,621评论 2 380

推荐阅读更多精彩内容