• Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或者Error都要难一些,主要是由于错误产生的root cause不是很显而易见。由于没有办法能够...

    在Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或者Error都要难一些,主要是由于错误产生的root cause不是很显而易见。由于没有办法能够直接拿到用户的内存dump文件,如果错误发生在线上的版本,分析起来就会更加困难。本文从一个具体的案例切入,介绍OOM分析的思路及相关工具的使用。

    案例背景


    在美团App 7.4~7.7版本期间,美食业务的OOM数量居高不下,远高于历史水平,主要都是DECODE本地的资源出错。

    图中OOM数量为各版本发版后第一个月的统计量,包含新发版本及历史版本。对比了同时期其他业务的情况,也有类似OOM。由于美食业务的访问量占美团App的比重较大,因此,OOM的数量相对其他业务也多一些。


    思路方案


    在问题较为严重的7.6~7.7版本期间,团队对OOM频现的原因有过各种猜测。笔者怀疑过是否是业务上某些修改引起的,例如头图尺寸变大,或者是由页面模块加载方式引起的等等。但这些与OOM问题出现的时间并不吻合。其次也怀疑过是否由某些ROM的Bug导致,但此推断缺乏有力的证据支撑。因此,要找到OOM的root cause,根本途径还是找到谁占的内存最多,然后再根据具体case具体分析,为什么占了这么多。


    采集用户手机内存信息


    要分析内存的占用,需要内存的dump文件,但是dump文件一般都比较大,让用户配合上传dump文件不合适。所以希望能够运行时采集一些内存的特征然后随着crash日志上报上来。当用户发生OOM时,dump出用户的内存,然后基于com.squareup.haha:haha:2.0.3分析,得到一些关键数据(内存占用最多的实例及所占比例等)。但这个方案很快就被证明是不可行的。主要基于下面几个原因:

    • 需要引入新的库。

    • dump和分析内存都很耗时,效率难以接受。

    • OOM时内存已经几乎耗尽,再加载内存dump文件并分析会导致二次OOM,得不偿失。


    模拟复现OOM


    采集用户手机内存信息的方案不可行,那么只能采取复现用户场景的方式。由于发生OOM时,用户操作路径的不确定性,无法精确复现线上的OOM,因此采取模拟复现的方式,最终发生OOM时的栈信息基本一致即可。为了能够尽量模拟用户发生OOM的场景,需要基本条件基本一致,即用户使用的手机的各种相关参数。


    挖掘OOM特征


    分析7.4以来的OOM,列出发生OOM的机器的特征,主要是内存和分辨率,适当考虑其它因素例如系统版本。


    这些特征可以总结为:内存一般,分辨率偏高,OOM的堆栈log基本一致。其中,OPPO N1(T/W)上所发生的OOM比重较高,约为65%,因此选定这款机器作为复现OOM的机器。


    关键数据(内存dump文件)


    需要复现OOM然后获取内存dump。思路是采取内存压力测试,让问题暴露的快速且充分。具体方案为:

    • 选取图片资源多且较为复杂的页面,比如美食的POI详情页。

    • 加载30次该页面,为了增加OOM的几率,30个POI页面的ID是不同的。


    OOM发生后,使用Android Studio自带的Android Monitor dump出HPROF文件,然后使用SDK中的hprof-conv(位于sdk_root/platform-tools)工具转换为标准的Java堆转储文件格式,这样可以使用MAT(Eclipse Memory Analyzer)继续分析。


    切到histogram视图,按shadow heap降序排列。


    选取byte数组,右击->list objects->with incoming references,降序排列可以看到有很多大小一致的byte[]实例。


    右击其中一个数组->Path to GC Roots-> exclude xxx references


    如上图所示,这些byte[]都是系统的EdgeEffect的drawable所持有,drawable对应的bitmap占用的空间为1566 * 406 * 4 = 2543184,与byte数组的大小一致。

    再看另外一个:


    这些byte[]是被App的一个背景图所持有,如下图:


    通过ImageView的ID(如图)及build目录下的R.txt反查可知该ImageView的ID名称,即可知其设置的背景图的大小为720 * 200(xhdpi),加载到内存并考虑density,size刚好是1080 * 300 * 4 = 1296000,与byte数组大小一致。


    数据分析


    为什么会出现这些大小一致的byte数组,或者说,为什么会创建多份EdgeEffect的drawable?查看EdgeEffect的源码(4.2.2)可知,其drawable成员也是通过Resources.getDrawable系统调用获取的。


    不论是Resources.getDrawable还是TypedArray.getDrawable,最终都会调用Resources.loadDrawable。继续看Resources.loadDrawable的源码,发现的确是使用了缓存。对于同一个drawable资源,系统只会加载一次,之后都会从缓存去取。


    既然drawable的加载机制并没有问题,那么drawable所在的缓存实例或者获取drawable的Resources实例是否是同一个呢?通过下面的代码,打印出每个Activity的Resources实例及Resources实例的drawable cache。

    //noinspection unchecked
    LongSparseArray<WeakReference<Drawable.ConstantState>> cache = (LongSparseArray<WeakReference<Drawable.ConstantState>>) Hack.into(Resources.class).field("mDrawableCache").get(getResources());
    Object appCache = Hack.into(Resources.class).field("mDrawableCache").get(getApplication().getResources());
    Log.e("oom", "Resources.mDrawableCache: {application=" + appCache + ", activity=" + cache + "}");
    Log.e("oom", "Resources: {application=" + getApplication().getResources() + ", activity=" + getResources() + "}");

    这也进一步解释了另外一个现象,即这些大小相同的数组的个数基本和启动Activity的数量成正比。


    通过数据分析可知,这些drawable之所以存在多份,是因为其所在的Resources实例并不是同一个。进一步debug可知,Resources实例存在多个的原因是开启了标志位sCompatVectorFromResourcesEnabled

    虽然最终造成OOM突然增多的原因只是开启一个标志位,但是这也告诫大家阅读API文档的重要性,其实很多时候API的使用说明已经明确告知了使用的限制条件甚至风险。


    7.8版本关闭了此标志,发版后第一个月的OOM数量(包含历史版本)为153,如下图。

    其中新版本发生的OOM数量为22。


    总结


    对于线上出现的OOM,如何分析和解决可以大致分为三个步骤:

    1. 充分挖掘特征。在挖掘特征时,需要多方面考虑,此过程更多的是猜测怀疑,所以可能的方面都要考虑到,包括但不限于代码改动、机器特征、时间特征等,必要时还需要做一定的统计分析。

    2. 根据掌握的特征寻找稳定的复现的途径。一般需要做内存压力测试,这样比较容易达到OOM的临界值,只是简单的一些正常操作难以触发OOM。

    3. 获取可分析的数据(内存dump文件)。利用MAT分析dump文件,MAT可以方便的按照大小排序实例,可以查看某些实例到GC ROOT的路径。


    From:https://toutiao.io/shares/1013011/url
    展开全文
  • 产生原因OOM产生可能的原因 OOM产生的本质是什么 如何解决和避免OOM 内存泄露问题
  • android处理oom异常 2016-04-22 21:02:55
    之前面试几家都问道了如何处理OOM异常,当时问答的不够好,所以今天查了很到资料... Android oom 有时出现很频繁,这一般不是Android设计的问题,一般是我们的问题。  就我的经验而言,出现oom,无非主要是以下几个
  • Android 关于OOM的解决方案 2014-02-19 13:20:20
    Android平台上面,应用程序OOM异常永远都是值得关注的问题。通常这一块也是程序这中的重点之一。这下我就如何解决OOM作一点简单的介绍。  首先,OOM就是内存溢出,即Out Of Memory。也就是说内存占有量超过了VM所...
  • android解决OOM 2020-05-29 23:31:30
    android解决OOM
  • Android中如何避免OOM 异常 2016-04-10 00:51:17
    OOM 内存溢出,想要避免OOM 异常首先我们要知道什么情况下会导致OOM 异常。 1、图片过大导致OOM Android 中用bitmap 时很容易内存溢出, 比如报如下错误: Java.lang.OutOfMemoryError : bitmap size exceeds VM ...
  • OOM android 2016-01-03 19:45:07
    1.【原创】Android 系统稳定性 - OOM(一) 2.【原创】Android 系统稳定性 - OOM(二)     3.Android内存泄露分析(MemoryAnalyzer工具)
  • Android内存优化之OOM 2015-09-22 13:58:06
    Android内存优化之OOM Android的内存优化是性能优化中很重要的一部分,而避免OOM又是内存优化中比较核心的一点,这是一篇关于内存优化中如何避免OOM的总结性概要文章,内容大多都是和OOM有关的实践总结概要。理解...
  • Androidoom详解 2020-04-28 22:28:41
    Androidoom原因 1.资源对象没关闭造成的内存泄露,try catch finally中将资源回收放到finally语句可以有效避免OOM。资源性对象比如: 1-1,Cursor 1-2,调用registerReceiver后未调用unregisterReceiver() 1...
  • Android关于OOM 2018-09-02 04:24:22
    OOM 内存溢出(Out Of Memory) 也就是说内存占有量超过了VM所分配的最大 Android和java中都会出现由于不良代码引起的内存泄露,为了使Android应用程序能够快速高效的运行,Android每个应用程序都会有专门Dalvik...
  • 一次解决Android OOM的经历 2019-07-12 20:40:22
    **OOM(Out Of Memory)**是Android应用开发中相信每个人都遇到过的问题,而OOM在crash log中的stack trace一般没有实际意义,因为是在分配内存的时候才会抛出OOM异常,而这个时候的stack trace和OOM的原因没有任何...
  • Android OOM分析 2016-10-18 20:08:03
    由之前Android sdk文档译文可知,Android设备为了保证多个APP的运行,保证足够RAM的空间,对每个APP的运行时Java堆大小做出了限制,当你的APP申请的JAVA堆空间,超过了设备的Java堆大小 时,系统就会抛出OOM异常,...
  • Android常见OOM原因整理 2019-01-12 14:48:52
    简单记录一下之前碰到过OOM问题的引起原因: 加载大图,项目中有可能会碰到加载高清图片或是长图(微博那种),在低端机上很容易直接OOM,所以要先对bitmap进行压缩再放入imageView中显示。 Bitmap使用完没有释放...
  • 对图片进行了base64加密,使用post add body进行上传。当选择图片过多时会 报oom,想多开线程进行传输,如何控制线程的数量?现在是每张图片都new了一个 线程,图片多了还是会崩?如何控制?求代码。
  • Android OOM原因总结 2019-06-03 17:26:09
    一、什么是OOMOOM(out of memory)即内存溢出。一个程序中,已经不需要使用某个对象,但是因为仍然有引用指向它垃圾回收器就无法回收它,当该对象占用的内存无法被回收时,就容易造成内存泄露。 Android的一个应用...
  • android 图片转base64 OOM 2016-08-24 00:56:25
    OOM异常,请问各位大师有什么解决办法? OOM截图: ![图片说明](https://img-ask.csdn.net/upload/201608/24/1471999999_344782.png) private String encode(String path) { Bitmap bitmap = null; bitmap = ...
  • Dalvik对android应用程序的最大内存有限制,而解析图片又是比较耗资源的,比如说解析一个2048*1536的位图需要12M的内存,这通常会造成OOM。 解决方案:根据设备的分辨率降低待加载的图片的质量,比如说设备...
  • 加载的时候,就crash --- OOM  shortMsg:java.lang.OutOfMemoryError  longMsg:java.lang.OutOfMemoryError: bitmap size exceeds VM budget  stackTrace:java.lang.OutOfMemoryError: bitmap s
  • Android内存优化-2-OOM研究 2019-07-07 05:13:46
    为什么会有OOMandroid系统app的每个进程或者说每个虚拟机(一个进程一个虚拟机)有最大内存限制(不同手机/版本限制不同), 如果应用申请的内存超过了限制, 系统就会抛出OOM异常. 这个整个设备的身影内存没关系. 较早...
  • OOM原因和解决方案 2016-05-23 17:50:07
    Android开发中经常会遇到OOM,而且现在已经是Android面试必问的问题了。 OOM(out of memory)即内存泄露。一个程序中,已经不需要使用某个对象,但是因为仍然有引用指向它垃圾回收器就无法回收它,当该对象占用的...
1 2 3 4 5 ... 20
收藏数 30,283
精华内容 12,113
热门标签