Android Memory Management

发表于:2013-04-07来源:futurexiong的博客作者:futurexiong点击数: 标签:软件测试Android
从早期G1的192MB RAM开始,到现在动辄1G -2G RAM的设备,为单个App分配的内存从16MB到48MB甚至更多,但OOM从不曾离我们远去。这是因为大部分App中图片内容占据了50%甚至75%以上,而App内容的极大丰富,所需的图片越来越多,屏幕尺寸也越来越大分辨率也越来越

  从早期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。

原文转自:http://my.eoe.cn/futurexiong/archive/1299.html