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
内容,那么找起来会方便很多。下面是一个查看的方法:
结尾
整个过程我基本是参照Google官方文档 Investigating Your RAM Usage,可以读下这份文档。
好啦,文章写到这里就结束了,如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
不管怎么说,大家技术还是要学好的。小编下面给大家分享一份成为高级工程师学习路线,如果想学习高级UI、性能优化、移动架构师、 NDK、混开发等Android高阶开发的朋友可以加下我的Android架构群:887084983,还有免费的学习资料及面试资料领取~
Android架构师之路很漫长,一起共勉吧!
相关推荐
JVM的基础和调优【JMM 内存结构 GC OOM 性能调优 ThreadLocal】 内存泄露:是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄露, 一次的内存泄露似乎不会有大的影响,但是内存泄露堆积的后果就是内存...
《Android 360°全方面性能调优》是一本深度探讨Android系统性能优化的宝典,涵盖了设计思想、代码优化、程序性能、内存管理、功耗控制、网络通信、应用打包、屏幕适配、启动速度、流畅度、ANR问题、崩溃监控、OOM...
"博客资源:ViewPager加载大量图片oom解决方案demo" 这个标题表明了这是一个关于解决在Android应用中使用ViewPager展示大量图片时出现内存溢出(Out Of Memory,简称OOM)问题的实例教程。ViewPager是Android SDK中...
HBase性能调优是一个多维度的过程,需要结合实际的应用场景、数据特点和业务需求来进行综合考量。正确的前期规划设计,合理的参数配置以及准确的问题定位都是提升HBase性能不可或缺的部分。通过不断地测试和调整,...
在Android开发中,"OOM"(Out of Memory)是一个常见的问题,它指的是应用程序在...以上就是解决Android OOM的一些关键知识点,通过合理运用这些策略,开发者可以有效地减少和预防OOM问题,提升应用的性能和稳定性。
基本上解决了OOM问题 如果 方便可以直接引用BitmapManager类到 项目中使用 解决blog 地址http://www.cnblogs.com/liongname/articles/2345087.html
9. **内存分析工具**:Android Studio提供了内存分析工具,可以帮助开发者定位内存泄漏,查看内存使用情况,及时发现并解决OOM问题。 10. **生命周期管理**:确保在Activity或Fragment的生命周期方法中正确释放资源...
Hive性能调优是一个复杂但关键的环节,涉及对Hive的参数配置以及针对应用程序的设计与开发进行优化。Hive是一个数据仓库基础工具,用于将结构化数据映射成数据库表,并通过HiveQL(简称HQL)查询语言执行数据处理...
解决Android中的图片加载与OOM问题,开发者需要采用合适的图片加载库,如Android-Universal-Image-Loader,利用其提供的内存和磁盘缓存机制、图片解码策略以及异步加载等功能,来优化图片处理过程,提升应用性能和...
JVM(Java Virtual Machine)性能调优是提升Java应用程序效率的关键环节,主要涉及内存管理和垃圾回收机制的优化。本文将详细解析JVM性能调优分析的过程,并提供常见参数选择配置的建议。 首先,进行JVM参数调优时...
2. **GIF解码优化**:GIF图片的每一帧都是一个独立的图像,如果一次性全部加载,会占用大量内存。选择支持按需解码的库,可以逐帧加载并显示,降低内存占用。 3. **内存缓存策略**:合理设置内存缓存策略,避免...
本文主要探讨Spark的性能优化策略,包括常规性能调优、算子调优、shuffle调优、JVM调优,以及如何防止数据倾斜和Out Of Memory (OOM)问题,所有内容都源于实际生产环境的一线经验。 1. **常规性能调优** - **最优...
Android 加载大图片 OOM 异常解决方案 在 Android 开发中,加载大图片是一个常见的问题,这可能会引发 OOM(Out of Memory)异常。OOM 异常是指应用程序试图分配超过系统可用内存的内存空间,从而导致应用程序崩溃...
JVM(Java Virtual Machine)是Java程序运行的基础,它提供了执行Java字节码的环境。...总之,理解和掌握JVM的工作原理、内存管理、垃圾回收以及调优策略,对于解决Java应用性能问题和提升系统稳定性至关重要。
Java性能调优主要聚焦在JVM的优化,其中包括内存模型的理解、垃圾收集(GC)机制以及如何处理常见的Out of Memory (OOM)异常。首先,我们要理解JVM的内存模型,这是进行性能调优的基础。 JVM内存模型由堆、栈、方法...
- 日志跟踪是性能调优的重要环节,对关键错误编号(如)进行跟踪和解决,是保证系统稳定运行的必要步骤。 7. 安全和资源管理 - 安全是性能调优不可忽视的一部分,确保应用程序和服务器的安全性,可以防止潜在的...
为了提高Spark作业的执行效率,开发者需要掌握一系列性能调优的原则和技巧。本文将围绕“Spark性能调优的几大原则”这一主题,详细介绍八大核心原则及其应用场景。 #### 二、避免创建重复的RDD **原则概述:** 在...
在Android开发中,ListView是一个非常重要的控件,用于展示大量数据列表。本教程将深入讲解如何使用ListView,结合自定义适配器以及解决Out-Of-Memory(OOM)问题。 首先,`ListView`是一个可滚动的视图,它能够...
- **数据分页**:为了减少一次性加载大量数据导致的内存压力,可以采用分页加载策略,只加载用户可视范围内的数据。 5. **性能监控**: - **Memory Profiler**:Android Studio内置的内存分析工具,可以帮助检测...
`oom_adj`(Out-of-Memory Adjuster)是Android用来管理进程优先级和内存分配的一个关键参数。较高的`oom_adj`值意味着该应用在内存清理过程中具有更高的保活优先级。以下是如何在Android 11源码中实现这一目标的...