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

手机游戏开发者 2024-9-6 22:57:01 5 0 来自 中国
作者:王晨彦
开篇

一天,后台统计到线上有大量 OOM 瓦解,小王收到老板的告急指令,立即排查!
小王心想,这还不简朴,待我看看瓦解堆栈,分分钟办理。
于是小王不慌不忙的打开瓦解后台,一看傻眼了,同样的 OOM,却有几十种不同的堆栈,大到创建 View,小到 new 一个 String。
小王差点骂了出来:这 OOM 不讲武德啊!
骂完之后,照旧得办理题目啊,否则怎么面对老板啊。
心路历程

正担心着,小王突然想起曾经看过性能优化的文章,内里先容了 Android Studio 中集成的 Profiler 可以分析 APP 内存。
既然堆栈看不出什么题目,那就只能照着文章的方法,碰碰运气了。
于是小王点开了 IDE 底部谁人毫不起眼的「Profiler」面板,映入眼帘的是:
小王一眼就看到了 MEMORY 栏,这不就是内存利用嘛。
2.png 嗯,数据倒是挺全,但是怎么知道那里导致 OOM 了啊,小王又开始猜疑人生了…
“放着不动肯定看不出什么啊,内存是动态申请的嘛。”
小王心想,既然这么多 OOM,那么肯定是 APP 内的常用页面导致的,于是小王开始一边往返切换常用页面,一边观察内存走势。
颠末多次实行,小王发现应用的内存占用确着实不绝升高,纵然手动 GC 之后,仍然居高不下。
小王想起口试宝典中「无法被 GC 接纳的对象,会导致内存泄漏」,于是手动点了下 GC,克制数据不精确。
4.png 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 导致内存泄漏了。固然又进步了一大步,但照旧找不到缘故原由。
小王发现,点击对象,可以查察全部实例的引用链,这下可把小王高兴坏了,而且小王还发现了一个非常可疑的引用链。
6.png 这不是 Coil 的 Memory Cache 嘛,但是这里明显是有缓存的嘛,怎么还会泄漏,难不成是这个开源库有 bug?
https://github.com/coil-kt/coil
小王怀着忐忑的心情打开了 RealMemoryCache 这个类。
7.png 这不就是一个基于 LRU 实现的内存缓存嘛,乍一看好像没什么弊端。
没时间仔细研究了,小王心想,先看看开源社区有没有人反馈过这个题目,小王过滤了一下包罗 "memory leak" 关键字的 issue。
8.png 果然有一个 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,是的话就直接开释了,克制造成泄漏。
末了小王高高兴兴的放工了。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-19 04:24, Processed in 0.350108 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表