专栏首页Android进阶之路全方位性能调优:一次解决OOM的经历
原创

全方位性能调优:一次解决OOM的经历

OOM

OOM(Out Of Memory)是Android应用开发中相信每个人都遇到过的问题,而OOM在crash log中的stack trace一般没有实际意义,因为是在分配内存的时候才会抛出OOM异常,而这个时候的stack trace和OOM的原因没有任何关系。所以OOM问题的定位和分析就需要多花费一些功夫。

下面,我就结合一个例子,来讲讲怎么定位OOM问题。

问题

在程序员们把代码写完,基本流程测试无误,准备要发布的时候,云测的结果却是:一大波OOM异常。没办法,只好重新打开电脑定位问题。 由于不是我一个人写的代码,所以直接看代码定位问题有点困难,这个时候就要上工具了。

1. 定性问题

1.1 定位问题,先定性

这是一个内存泄漏导致的OOM,还是应用本身设计不当,导致一次需要加载的内存过多,导致的OOM?

定位问题的方法很简单,看内存是不是一直在增长,如果在使用的过程中内存一直在增长,则很有可能是内存泄漏导致的。

1.2 推荐使用的工具

Android Studio自带的Memory Monitor,很简单的一个小工具,能够把应用内存实时展现出来,简单到我认为不需要多说了。如下图:

在摆弄了几分钟之后,我发现了几个问题:

1. app刚刚启动的时候,内存占用就很大,因为用了很多的贴图(设计不当)

2. app在进入播放界面并且推出之后,即使什么操作都不做,内存一直在缓慢增长(内存泄漏)

3. app在我退出再进的时候,内存占用几乎翻番(内存泄漏)

其中,问题2很快就能猜出来,播放结束后MediaPlayer没有被释放,之后验证了下,解决。

2. 设计不当?优化!

这个问题就很泛了,比如,纯色的背景就不用图片来实现,不用超过需要像素值的图片,不加载显示范围之外的图片,等等。

最终找到了几张图片,只需要1280x720,给的图是1920x1080。同时简单实现了图片的LazyLoading。问题1基本凑合搞定。

3. 内存泄漏?

如果在使用过程中,内存曲线一直是上涨趋势,这就很有可能存在内存泄漏了。

3.1 查看堆的信息

Android SDK的工具集中就提供这样一个工具:Device Monitor。使用方法如下:

1. 打开Android Device Monitor

2. 选择你要调试的进程

3. 点击Update Heap按钮

基本情况如下图:

可以看到基本的堆情况,以及堆内对象的概览。

3.2 查看Activity泄漏

常见的内存泄漏很多都是由于Activity对象不能被释放导致的,用下面的adb命令可以快速的定位到这个问题:

adb shell dumpsys meminfo <package_name>

得到结果如下:

** MEMINFO in pid 9953 [com.google.android.gm] **
                 Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
               Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
              ------  ------  ------  ------  ------  ------  ------  ------  ------
  Native Heap      0       0       0       0       0       0    7800    7637(6)  126
  Dalvik Heap   5110(3)    0    4136    4988(3)    0       0    9168    8958(6)  210
 Dalvik Other   2850       0    2684    2772       0       0
        Stack     36       0       8      36       0       0
       Cursor    136       0       0     136       0       0
       Ashmem     12       0      28       0       0       0
    Other dev    380       0      24     376       0       4
     .so mmap   5443(5) 1996    2584    2664(5) 5788    1996(5)
    .apk mmap    235      32       0       0    1252      32
    .ttf mmap     36      12       0       0      88      12
    .dex mmap   3019(5) 2148       0       0    8936    2148(5)
   Other mmap    107       0       8       8     324      68
      Unknown   6994(4)    0     252    6992(4)    0       0
        TOTAL  24358(1) 4188    9724   17972(2)16388    4260(2)16968   16595     336

 Objects
               Views:    426         ViewRootImpl:        3(8)
         AppContexts:      6(7)        Activities:        2(7)
              Assets:      2        AssetManagers:        2
       Local Binders:     64        Proxy Binders:       34
    Death Recipients:      0
     OpenSSL Sockets:      1

 SQL
         MEMORY_USED:   1739
  PAGECACHE_OVERFLOW:   1164          MALLOC_SIZE:       62

其中的ViewRootImpl、Activities、AppContexts数量很值得关注。 关于其他的输出含义,见developer docs

3.3 取heap dump

在基本确定内存有泄漏之后,就需要定位具体是哪个对象泄漏,好定位相关代码,这个时候就可以对heap dump进行分析。

heap dump就是一个内存heap的快照,可以用来很具体的分析内存里到底有什么。

首先,我们用Device Manager需要导出一个heap dump,如图所示

然后转化成java dump,用 Eclipse Memory Analyzer Tool (MAT) 来分析。

用platform-tools里的hprof-conv来转化:

hprof-conv heap-original.hprof heap-converted.hprof

转化完成之后,我们就可以用MAT来打开分析。

4. Eclipse Memory Analyzer Tool (MAT)

当我们用MAT打开dump时,看到的基本界面如下:

这里有几个概念需要了解:

1. Shallow Heap & Retained Heap

  • Shallow Heap的大小就是对象所占的内存空间,一般一个Object持有一个引用会需要32或者64bit的空间(取决于JVM)。
  • 由于A对象持有B对象引用会导致B对象在GC中不会被销毁,所以由于被对象直接或者间接持有引用而不会被释放的对象的占用的内存总和,就是Retained Heap。
  • 简单来说,Shallow heap就是对象占用的空间,Retained Heap就是假如对象被释放,连带能够释放出来的空间。

2. Dominator Tree

  • 定义在这里。简单来说,Dominator Tree可以很好地观察Retained Heap大小。
  • 通常,在dump中查看Histogram和dominator_tree就可以看出一些端倪,例如这个例子中,histogram图中,占用内存最多的是byte[]对象,通过右键 -> List object菜单,可以看到

基本上都是Bitmap对象,而且Bitmap有重复数据:

到这一步,可以继续追查是谁导致两份相同的数据不能得到释放,通过 右键 -> Path to CG root 功能,可以追查到最终是被哪个对象持有导致不能被释放,结果如下:

到这里,问题基本就明白了:

DBHelper是一个单例静态对象,这个对象会持有一个Context对象,在代码中被当成Context传入DBHelper的是一个Activity对象,所以这个Activity不会被释放;当这个Activity被销毁重建时,新的Activity会重新加载ContentView,而老的Activity所持有的整个View的树全部不会被释放,同时View持有的图片也不会被释放,导致内存不够。

至此,整个问题基本已经找到,同时告诉我们,用单例最好不要持有Context对象,如果需求需要,查看下这个设计是否合理,以及使用的时候谨慎。

一个常用的MAT技巧

在分析内存占用的时候,通常可以看到各种占用最多的时Bitmap对象,这个时候,如果能直接显示这个Bitmap内容,那么找起来会方便很多。下面是一个查看的方法:

stackoverflow上回答见这里

结尾

整个过程我基本是参照Google官方文档 Investigating Your RAM Usage,可以读下这份文档。

最后

如果你看到了这里,觉得文章写得不错就点个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

最后针对Android开发的同行,小编这边给大家整理了一些资料,其中分享内容包括但不限于【高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术】希望能帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也是可以分享给身边好友一起学习的!

为什么某些人会比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!希望读到这的您能转发分享订阅一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+订阅,第一时间获得更新的知识点

Android架构师之路很漫长,一起共勉吧!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android性能调优:记一次解决OOM的经历

    OOM(Out Of Memory)是Android应用开发中相信每个人都遇到过的问题,而OOM在crash log中的stack trace一般没有实际意义,...

    Android技术干货分享
  • 如何准备Java面试?如何把面试官的提问引导到自己准备好的范围内?如何在面试中介绍自己的项目经验在面试中如何展示虚拟机和内存调优技能内部类、final与垃圾回收,面试时你一说,面试官就知道

    Java能力和面试能力,这是两个方面的技能,可以这样说,如果不准备,一些大神或许也能通过面试,但能力和工资有可能被低估。再仔细分析下原因,面试中问的问题...

    用户1153489
  • 首次揭秘,快手自研性能监控系统开源项目KOOM

    随着用户数量扩大,业务不断发展,移动端技术也在突飞猛进中暴露出了诸多“疑难杂症”,比如应用的稳定性如何保障、性能如何提升等等,都是移动端领域需要重点攻克的技术方...

    深度学习与Python
  • iOS 性能优化实践:头条抖音如何实现 OOM 崩溃率下降50%+

    OOM 其实是Out Of Memory的简称,指的是在 iOS 设备上当前应用因为内存占用过高而被操作系统强制终止,在用户侧的感知就是 App 一瞬间的闪退,...

    ios-lan
  • 在面试中如何展示虚拟机和内存调优技能

    在这个寒冬中,我们得不断提升自己的能力,所以这次,我就针对“虚拟机描述”这个点扩展出去,具体讲些在面试中展示虚拟机内存调优能力的话术。

    本人秃顶程序员
  • 抖音 Android 性能优化系列:Java 内存优化篇

    在未对抖音内存进行专项治理之前我们梳理了一下整体内存指标的绝对值和相对崩溃,发现占比都很高。另外,内存相关指标在去年春节活动时又再次激增达到历史新高,所以整体来...

    字节流动
  • TKE常见问题以及故障定位

    建议:暂时没有完美解决方案,可通过 Pod 反亲和打散 client 避免流量集中规避

    蒋经纬
  • Android进阶:Android内存管理之道

    对于移动应用开发,不管是Android还是IOS,内存都是永远的痛。但是合理的编写代码,会避免OOM的出现。 相信一步步走过来的Android从业者,每个人都...

    非著名程序员
  • 深入探索 Android 内存优化(炼狱级别-上)

    本篇是 Android 内存优化的进阶篇,难度可以说达到了炼狱级别,建议对内存优化不是非常熟悉的仔细看看前篇文章: Android性能优化之内存优化,其中详细分...

    做个快乐的码农
  • Android 内存暴减的秘密?!

    在 我这样减少了26.5M Java内存!中内存优化一期已经告一段落,主要做的事情是,造了几个分析内存问题的轮子,定位进程各种类型内存占用情况,分析了线程创建O...

    WeTest质量开放平台团队
  • Android 内存暴减的秘密?!

    商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处。

    WeTest质量开放平台团队
  • 在面试中如何展示虚拟机和内存调优技能最近面试java后端开发的感受:如果就以平时项目经验来面试,通过估计很难——再论面试前的准备

    最近看到我的博文里,最近面试java后端开发的感受:如果就以平时项目经验来面试,通过估计很难——再论面试前的准备,这篇博文,推荐数最多,77推荐0反对...

    用户1153489
  • android开发性能分析

    1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾。不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结、我一总结的都说到了很多优化注意事项...

    xiangzhihong
  • OOM Killer的一点分析

    最近线上遇到了好几次由于内存泄漏导致OOM的问题,且大部分都是整个模块被kill掉woker进程,只剩下接入的epoll进程和统计进程的情况,从而导致拨测程序在...

    暴雨一场
  • Android OOM案例分析

    在Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Except...

    美团技术团队
  • 微信 Android 终端内存优化实践

    前言 内存问题是软件领域的经典问题,平时藏得很深,在出现问题之前没太多征兆。而一旦爆发问题,问题来源的多样、不易重现、现场信息少、难以定位等困难,就会让人头疼...

    微信终端开发团队
  • 三位架构师转正的一些感悟

    翟凤玺
  • 移动开发:如果没做好这些准备及面试题,找工作还是先缓缓吧

    根据回忆写下的面试要点,都是比较常见的问题。大家可以对照回答下,不清楚的可以查下资料补充下。

    Android技术干货分享
  • 终于有人把Java虚拟机讲清楚了!

    很多人学习一门技术,更多的是看视频看书,纯理论学习。背概念,缺乏真实的JVM生产案例实战。很多同学看过不少JVM书籍或视频,理论知识丰富。但我们实际工作中会遇到...

    JAVA葵花宝典

扫码关注云+社区

领取腾讯云代金券