`
king_tt
  • 浏览: 2232885 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Android OOM介绍及分析方法

 
阅读更多

从早期G1的192MB RAM开始,到现在动辄1G -2G RAM的设备,为单个App分配的内存从16MB到48MB甚至更多,但OOM从不曾离我们远去。这是因为大部分App中图片内容占据了50%甚至75%以上,而App内容的极大丰富,所需的图片越来越多,屏幕尺寸也越来越大分辨率也越来越高,所需的图片的大小也跟着往上涨,这在大屏手机和平板上尤其明显。而且还经常要兼容低版本的设备。所以Android的内存管理显得极为重要。



在这里我们主要讲两件事情:
1.Gingerbread和Honeycomb中的一些影响你使用内存的变化
-heap size
-GC
-bitmaps
2.理解heap的用途分配
-logs
-merory leaks
-Eclispe Memory Analyzer(MAT)



首先第一部分,我们都知道Android是个多任务操作系统,同时运行着很多程序,都需要分配内存,不可能为一个程序分配越来越多的内存以至于让整个系统都崩溃,因此heap的大小有个硬性的限制,跟设备相关,从发展来说也是越来越大,G1:16MB,Droid:24MB,Nexus One:32MB,Xoom:48MB,但是一旦超出了这个使用的范围,OOM便产生了。如果你正在开发一个应用,想知道设备的heap大小的限制是多少,比方说根据这个值来估算自己应用的缓存大小应该限制在什么样一个水平,你可以使用ActivityManager.getMemoryClass ()来获得一个单位为MB的整数值,一般来说最低不少于16MB,对于现在的设备而言这个值会越来越大,24MB,32MB,48MB甚至更大。



但是对于一些内存非常吃紧的比如图片浏览器等应用,在平板上所需的内存更大了。因此在Honeycomb之后AndroidManifest.xml增加了largeHeap的选项



1
2
3
4
<application
       android:largeHeap="true"
       ...
</application>



这允许你的应用使用更多的heap,可以用ActivityManager.getLargeMemoryClass ()返回一个更大的可用heap size。但是这里要警告的是,千万不要因为你的应用报OOM了而使用这个选项,因为更大的heap size意味着更多的GC时间,意味着应用的性能越来越差,而且用户也会发现其他应用很有可能会内存不足。只有你需要使用很多的内存而且非常了解每一部分内存的用途,这些所需的内存都是不可或缺的,这个时候你才应该使用这个选项。



刚刚我们提到更大的heap size意味着更多的GC时间,下面我们来谈谈Garbage Collection。



1.jpg



如上图所示,GC会选择一些它了解还存活的对象作为内存遍历的根节点,比方说thread stack中的变量,JNI中的全局变量,zygote中的对象等,然后开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。如下图蓝色部分。



2.jpg



因此也可以看出,更大的heap size需要遍历的对象更多,回收垃圾的时间更长,所以说使用largeHeap选项会导致更多的GC时间。



在Gingerbread之前,GC执行的时候整个应用会暂停下来执行全面的垃圾回收,因此有时候会看到应用卡顿的时间比较长,一般来说>100ms,对用户而言已经足以察觉出来。Gingerbread及以上的版本,GC做了很大的改进,基本上可以说是并发的执行,也不是执行完全的回收,只有在GC开始以及结束的时候会有非常短暂的停顿时间,一般来说<5ms,用户也不会察觉到。



在Honeycomb之前,Bitmap的内存分配如下图。



3.jpg



蓝色部分是Dalvik heap,黄色部分是Bitmap引用对象的堆内存,而Bitmap实际的Pixel Data是分配在Native Memory中。这样做有几个问题,首先需要调用reclyce()来表明Bitmap的Pixel Data占用的内存可回收,不调用这个方法的话就要靠finalizer来让GC回收这部分内存,但了解finalizer的应该都知道这相当的不可靠;其次是很难进行Debug,因为一些内存分析工具是查看不到Native Memory的;再次就是不调用reclyce()需要回收Native Memory中的内存的话会导致一次完整的GC,GC执行的时候会暂停整个应用。



Honeycomb之后,Bitmap的内存分配做出了改变,如下图



4.jpg



蓝色黄色部分没有变化,但Bitmap实际的Pixel Data的内存也同样分配在了Dalvik heap中。这样做有几个好处。首先能同步的被GC回收掉;其次Debug变得容易了,因为内存分析工具能够查看到这部分的内存;再次就是GC变成并发了,可做部分的回收,也就是极大缩短了GC执行时暂停的时间。



接下来我们讲第二部分。一般来说我们希望了解我们应用内存分配,最基本的就是查看Log信息。比方说看这样一个Log信息(这是Gingerbread版本的,Honeycomb以后log信息有改动):



D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/
9991K, external 4703K/5261K, paused 2ms 2ms



GC_XXX表明是哪类GC以及触发GC的原因。几种GC类型:
- GC_CONCURRENT:这是因为你的heap内存占用开始往上涨了,为了避免heap内存满了而触发执行的。
- GC_FOR_MALLOC:这是由于concurrent gc没有及时执行完而你的应用又需要分配更多的内存,内存要满了,这个时候不得不停下来进行malloc gc。
- GC_EXTERNAL_ALLOC:这是为external分配的内存执行的GC,也就是上文提到的Bitmap Pixel Data之类的。
- GC_HPROF_DUMP_HEAP:这是当你做HPROF这样一个操作去创建一个HPROF profile的时候执行的。
- GC_EXPLICIT:这是由于你显式的调用了System.gc(),这是不提倡的,一般来说我们可以信任系统的GC。



freed 2049K表明在这次GC中回收了多少内存。
65% free 3571K/9991K是heap的一些统计数据,表明这次回收后65%的heap可用,存活的对象大小3571K,heap大小是9991K。
external 4703K/5261K是Native Memory的数据。放Bitmap Pixel Data或者是NIO Direct Buffer之类的。第一个数字表明Native Memory中已分配了多少内存,第二个值有点类似一个浮动的阀值,表明分配内存达到这个值系统就会触发一次GC进行内存回收。
paused 2ms 2ms表明GC暂停的时间。从这里你可以看到越大的heap size你需要暂停的时间越长。如果是concurrent gc你会看到2个时间一个开始一个结束,这时间是很短的,但如果是其他类型的GC,你很可能只会看到一个时间,而这个时间是相对比较长的。



通过Log可以对内存信息有个基本的了解,但这不足以了解什么对象在使用内存,在哪使用了内存。这时候你需要用Heap Dumps。一个Heap Dump基本上来说就是一个包含你heap中所有对象信息的二进制文件。你可以用DDMS来生成这个文件,点击DDMS中下图的那个按钮。



5.jpg



同时Heap Dumps也有对应的API,你想要在特定的时间点获取一份Heap Dump,使用android.os.Debug.dumpHprofData()。获取到的文件要转换成标准的HPROF格式,使用如下命令:hprof-conv orig.hprof converted.hprof。然后用MAT或者jhat进行分析。



在讲MAT之前先讲下Memory Leaks。要清楚GC并不能防止Memory Leaks,所谓Memory Leaks就是引用到了已经没用的对象从而让这些对象避免了被GC回收,跟C/C++中的概念并不一样。容易导致内存泄漏的是一些Activity,Context,View,Drawable之类的引用,和一些非静态的内部类比方说Runnable之类的以及一些Caches。比如你旋转屏幕的时候在新的方向上产生一个新的Activity,如果有变量引用到旧的Activity就会导致其无法被GC,造成Memory Leaks。



通常通过上面介绍的Log信息,只要已用memory一直处于上升的情形而不回落,便大致能了解到应用存在Memory Leaks。不过MAT这类工具可以帮助你更好的对memory进行分析。使用MAT之前有2个概念是要掌握的:Shallow heap和Retained heap。Shallow heap表示对象本身所占内存大小,一个内存大小100bytes的对象Shallow heap就是100bytes。Retained heap表示通过回收这一个对象总共能回收的内存,比方说一个100bytes的对象还直接或者间接地持有了另外3个100bytes的对象引用,回收这个对象的时候如果另外3个对象没有其他引用也能被回收掉的时候,Retained heap就是400bytes。



MAT使用Dominator Tree这样一种来自图形理论的概念。



6.jpg



所谓Dominator,就是Flow Graph中从源节点出发到某个节点的的必经节点。那么根据这个概念我们可以从上图左侧的Flow Graph构造出右侧的Dominator Tree。这样一来很容易就看出每个节点的Retained heap了。Shallow heap和Retained heap在MAT中是非常有用的概念,用于内存泄漏的分析。



我们用Honeycomb3.0中的HoneycombGallery做一个Demo。在工程的MainActivity当中加入如下代码:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class MainActivity extends Activity implements ActionBar.TabListener {
    static Leaky leak = null;
    class Leaky {
        void doSomething() {
            System.out.println("Wheee!!!");
        }
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (leak == null) {
            leak = new Leaky();
        }
        ...



上面这段代码,对Java熟悉的同学都应该了解内部类对象持有了外部类对象引用,而leak作为静态变量在非空判断下只产生了一个对象,因此当旋转屏幕时生成新的Activity的时候旧的Activity的引用依然被持有,如下图:



8.jpg



通过观察旋转屏幕前后Log中GC的信息也能看出heap的分配往上涨了许多,并且在GC执行完heap的分配稳定之后并没有降下来,这就是内存泄漏的迹象。



我们通过MAT来进行分析。先下载MAT,可以作为Eclipse插件下载,也可以作为RCP应用下载,本质上没有区别。DDMS中选中应用对应的进程名,点击Dump HPROF file的按钮,等一小段时间生成HPROF文件,如果是Eclipse插件的话,Eclipse会为这个HPROF自动转化成标准的HPROF并自动打开MAT分析界面。如果是作为RCP应用的话,需要用sdk目录tools中的hprof-conv工具来进行转化,也就是上文提及的命令hprof-conv orig.hprof converted.hprof,这种方式保存HPROF文件的位置选择更为自主,你也可以修改Eclipse的设置让Eclipse提示保存而不是自动打开,在Preferences -> Android -> DDMS中的HPROF Action由Open in Eclipse改为Save to disk。打开MAT,选择转化好的HPROF文件,可以看到Overview的界面如下图:



7.jpg



中间的饼状图就是根据我们上文所说的Retained heap的概念得到的内存中一些Retained Size最大的对象。点击饼状图能看到这些对象类型,但对内存泄漏的分析还远远不够。再看下方Action中有Dominator Tree和Histogram的选项,这一般来说是最有用的工具。还记得我们上文说过的Dominator Tree的概念吗,这就是我们用来跟踪内存泄漏的方式。点开Dominator Tree,会看到以Retained heap排序的一系列对象,如下图:



9.png



Resources类型对象由于一般是系统用于加载资源的,所以Retained heap较大是个比较正常的情况。但我们注意到下面的Bitmap类型对象的Retained heap也很大,很有可能是由于内存泄漏造成的。所以我们右键点击这行,选择Path To GC Roots ->exclude weak references,可以看到下图的情形:



10.png



Bitmap最终被leak引用到,这应该是一种不正常的现象,内存泄漏很可能就在这里了。MAT不会告诉哪里是内存泄漏,需要你自行分析,由于这是Demo,是我们特意造成的内存泄漏,因此比较容易就能看出来,真实的应用场景可能需要你仔细的进行分析。



根据我们上文介绍的Dominator的概念,leak对象是该Bitmap对象的Dominator,应该出现在Dominator Tree视图里面,但实际上却没有。这是由于MAT并没有对weak references做区别对待,这也是我们选择exclude weak references的原因。如果我们Path To GC Roots ->with all references,我们可以看到下图的情形:



11.png



可以看到还有另外一个对象在引用着这个Bitmap对象,了解weak references的同学应该知道GC是如何处理weak references,因此在内存泄漏分析的时候我们可以把weak references排除掉。



有些同学可能希望根据某种类型的对象个数来分析内存泄漏。我们在Overview视图中选择Actions -> Histogram,可以看到类似下图的情形:



12.png



上图展示了内存中各种类型的对象个数和Shallow heap,我们看到byte[]占用Shallow heap最多,那是因为Honeycomb之后Bitmap Pixel Data的内存分配在Dalvik heap中。右键选中byte[]数组,选择List Objects -> with incoming references,可以看到byte[]具体的对象列表:



13.png
14.png



我们发现第二个byte[]的Retained heap较大,内存泄漏的可能性较大,因此右键选中这行,Path To GC Roots -> exclude weak references,同样可以看到上文所提到的情况,我们的Bitmap对象被leak所引用到,这里存在着内存泄漏。



15.png



在Histogram视图中第一行<Regex>中输入com.example.android.hcgallery,过滤出我们自己应用中的类型,如下图:



16.png



我们发现本应该只有一个MainActivity现在却有两个,显然不正常。右键选择List Objects -> with incoming references,可以看到这两个具体的MainActivity对象。右键选中Retained heap较大的MainActivity,Path To GC Roots -> exclude weak references,再一次可疑对象又指向了leak对象。



17.png
18.png
19.png



以上是MAT一些基本的用法,如果你感兴趣,可以自行深入的去了解MAT的其他功能。


分享到:
评论

相关推荐

    处理android bitmap oom 2.0版本

    - 使用Android Studio的内存分析工具,实时查看和分析内存使用情况,找出可能的内存泄漏。 综上所述,处理Android Bitmap OOM的关键在于合理控制Bitmap的内存占用,通过解码、缓存、回收和配置优化等方式进行有效...

    处理android bitmap oom

    10. **监控内存使用**:使用Android Studio的内存分析工具,定期检查和调试内存泄漏,确保Bitmap被正确释放。 以上策略结合使用,可以有效地处理Android中的Bitmap OOM问题。在开发过程中,要持续关注性能和内存...

    android 永远不会oom的瀑布流

    本篇文章将深入探讨如何构建一个“永远不会OOM”的瀑布流,并分析其中的关键技术点。 首先,我们需要理解Android的内存管理机制。Android系统使用Dalvik或ART虚拟机,它们都有一定的内存限制。当应用程序占用的内存...

    Android相册图片解决OOM问题

    9. **内存分析工具**:Android Studio提供了内存分析工具,可以帮助开发者定位内存泄漏,查看内存使用情况,及时发现并解决OOM问题。 10. **生命周期管理**:确保在Activity或Fragment的生命周期方法中正确释放资源...

    Android-OOM.rar_memory android_memory for Android_out

    下面将详细讨论Android OOM的原因、表现、诊断方法以及解决策略。 **1. OOM原因** Android设备的内存资源有限,当应用占用内存超过系统设定阈值时,就会触发OOM。主要原因包括: - 不合理的对象实例化:创建大量...

    Android 图片压缩不OOM,超高保真度

    通过分析和学习这个示例,开发者可以更好地理解和掌握Android图片压缩的技巧,以达到在不引发OOM的前提下,保持图片的高保真度。 总结来说,处理Android中的大图,我们需要合理地使用`BitmapFactory.Options`进行...

    ANDROIDBITMAP内存限制OOM,OUTOFMEMORY.pdf

    文档标题和描述中提到的“ANDROIDBITMAP内存限制OOM,OUTOFMEMORY”指的就是在处理位图(BITMAP)时超出了虚拟机(VM)的内存预算,导致系统抛出OutOfMemoryError异常。 根据给出的内容部分,我们可以推断出以下知识...

    图片oom,解决方法

    以下将详细探讨图片OOM的问题以及解决方法。 1. **问题分析**: - **图片加载机制**:Android系统在加载图片时,会将图片解码为Bitmap对象,而Bitmap占用的内存与图片的宽度、高度和颜色深度有关。高分辨率图片在...

    Android内存OOM优化详解.pdf

    本文将深入探讨Android内存管理的基础、内存优化策略、Bitmap的使用及管理、内存泄漏的原因和解决方案,以及如何进行内存分析。 首先,了解Android内存管理的基础至关重要。Android系统为每个应用程序分配了一个...

    Android高级应用源码-加载本地图片,绝对不会出现OOM.zip

    在Android开发中,加载本地图片是一项常见的任务,但如果不妥善处理,可能会导致内存溢出(Out Of Memory,简称OOM)问题。"Android高级应用源码-加载本地图片,绝对不会出现OOM.zip"是一个针对这一问题的解决方案,...

    安卓内存OOM分析

    分析内存泄露的方法有: 1. **内存分析工具**:如MAT(Memory Analyzer Tool)、LeakCanary等,可以帮助我们定位内存泄露的对象和引用链。 2. **Hprof文件分析**:通过dump hprof文件,可以获取到内存快照,进一步...

    bitmap OOM的解决方案

    10. **监控内存使用**:使用Android Studio的Profiler工具进行内存分析,找出内存泄漏和异常消耗的情况,及时优化。 在提供的Demo_BitmapOOM_Solution中,可能包含了上述部分或全部解决方案的示例代码,可以参考...

    gif加载动画不oom

    本文将详细介绍如何在Android中实现GIF加载而不触发OOM问题。 首先,我们需要理解为什么GIF加载容易导致OOM。GIF格式的图片包含了连续的帧,当使用默认的Bitmap解码方式时,会一次性加载所有帧到内存中,这样对于大...

    解决帧动画OOM问题

    8. **使用内存分析工具**:使用Android Studio的内存分析工具可以帮助我们找到内存泄漏的源头,及时优化代码。 9. **缓存策略**:可以考虑使用LRU缓存策略来管理帧图片,当内存不足时,优先清除最近最少使用的帧。 ...

    Android应用源码之加载本地图片,绝对不会出现OOM.zip

    在Android应用开发中,内存管理..."Android应用源码之加载本地图片,绝对不会出现OOM.zip"这个源码应该提供了上述策略的具体实现,你可以通过学习和分析源码,加深对Android图片加载优化的理解,并应用于自己的项目中。

    Android 加载本地图片,绝对不会出现OOM-IT计算机-毕业设计.zip

    10. **App论文撰写**:在毕业设计中,除了实现功能外,还需要撰写论文,介绍所采用的技术、优化措施以及实验结果分析,这部分需要理论与实践相结合,清晰阐述设计思路和改进点。 综上所述,这个毕业设计项目涵盖了...

    Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果(转)

    这篇博客“Android 自定义 HorizontalScrollView 打造再多图片(控件)也不怕 OOM 的横向滑动效果”提供了一个解决方案,通过自定义HorizontalScrollView来优化内存管理,防止OOM的发生。 首先,理解Android的内存...

    安卓Android源码——图片下载以及内存处理防OOM.zip

    4. **Memory Analyzer Tool (MAT)**:Android提供的内存分析工具,可以帮助开发者找到内存泄漏和不必要的内存占用,进行针对性优化。 5. **池化技术**:对于频繁创建和销毁的对象,如ImageView,可以使用对象池来...

    Android高级应用源码-图片下载以及内存处理防OOM.zip

    Android提供了一些方法来控制Bitmap的内存占用,例如使用BitmapFactory.Options设置解码时的配置,如inSampleSize,可以减小图片的大小,从而降低内存消耗。另外,及时地释放不再使用的Bitmap资源也非常重要,可以...

Global site tag (gtag.js) - Google Analytics