热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一次解决AndroidOOM的经历

OOM**OOM(OutOfMemory)**是Android应用开发中相信每个人都遇到过的问题,而OOM在crashlog中的stacktrace一般没有实际意义

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命令可以快速的定位到这个问题:
在这里插入图片描述
得到结果如下:
在这里插入图片描述
其中的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来转化:
在这里插入图片描述
转化完成之后,我们就可以用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,可以读下这份文档。


推荐阅读
  • 一面自我介绍对象相等的判断,equals方法实现。可以简单描述挫折,并说明自己如何克服,最终有哪些收获。职业规划表明自己决心,首先自己不准备继续求学了,必须招工作了。希望去哪 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • JVM:33 如何查看JVM的Full GC日志
    1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr ... [详细]
  • jvm内存区域与溢出为什么学习jvm木板原理,最短的一块板决定一个水的深度,当一个系统垃圾收集成为瓶颈的时候,那么就需要你对jvm的了解掌握。当一个系统出现内存溢出,内存泄露的时候 ... [详细]
  • Android系统启动过程分析一、Android平台架构首先贴一张Android系统架构图方便理解整个Android架构,这可以让我们从整体上对整个启动流程有个大概认知。可以看出整 ... [详细]
author-avatar
你就夜沉思
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有