Dalvik虚拟机有垃圾回收机制,但这并不意味着你可以忽略内存管理。尤其要注意在内存严重受限的移动设备上的内存用法。在这篇文章中,我们会一起来看一看AndroidSDK中的内存
分析工具,以帮助你优化程序内存用量。
一些内存使用的问题是非常明显的。比如,如果你的APP在被用户使用的任何时候泄露了内存,都可能会导致OutOfMemoryError,最终导致你的程序奔溃。其他的一些问题更微妙、不易察觉,可能是降低程
序(当垃圾回收器更频繁调用且花费更长时间)或系统性能。
工具使用
Android SDK提供了两种分析APP内存使用的方法:
①DDMS中的Allocation Tracker标签
②heap dumps(堆转储)
当你想获得在特定的时期哪些分配内存的行为正在发生,但又没有给你程序堆的整体信息时,Allocation Tracker就非常有用了。获取更多关于Allocation Tracker的信息请看文章:
TrackingMemory Allocations本文的其余部分主要关注heap dumps——一个功能强大的内存分析工具。
Heap dump(堆垃圾场)是一个被存储在二进制格式的HPROF文件中的应用程序堆的快照。Dalvik使用的与HPROF tool in Java的格式相似,但又不完全相同。有一些生成正在运行的Android程序的heapdump的方法。
第一种是使用在DDMS试图中的Dump HPROF file按钮。如果你需要当dump被创建时更精确的信息,你也可以用
android.os.Debug.dumpHprofData()功能编程,创建一个heap dump 。
分析heap dump时,你可以使用像jhat或者Eclipse Memory Analyzer (MAT).这样的基础工具。无论如何,第一次你都需要将Dalvik格式的.hprof文件转换成J2SE HPROOF格式。你可以使用Android SDK下的“hprof-conv”工具完成这项工
作。例如:
hprof-convdump.hprofconverted-dump.hprof
举个例子:调试内存泄露
在Dalvik运行时,开发者不能明确地分配和释放内存。因此,你不能像C或C++那样泄露内存。在你的代码中”内存泄露“是当你持有一个不再需要的对象的引用。有时,一个单一的引用能够预防大量的对象被垃圾回收。
让我们通过一个SDK中的HoneycombGallery sample app例子来试验一下。他是一个演示如何使用一些新的Honeycomb APIs的简单的画廊应用。我们故意在其中加入可以造成内存泄露的代码。
想象一下,当我们从网络获取图片,为了使它更加流畅,我们可能决定实现一个图片缓存器。我们对ContentFragment.java文件做了一些小改动。在类的顶端,加入一个新的静态变量
privatestaticHashMap<String,Bitmap> sBitmapCache =newHashMap<String,Bitmap>();
这就是我们的Bitmap缓存。现在我们可以修改updateContentAndRecyleBitmap()方法在加载之前检查cache,同时在加载完成后添加Bitmaps到cache。
void updateContentAndRecycleBitmap(int category, int position) {
if (mCurrentActionMode != null) {
mCurrentActionMode.finish();
}
// 获取需要绘制和更新到ImageView的Bitmap(Get the bitmap that needs to be drawn and update the ImageView.)
// 检查这个Bitmap是否已经在cache中存在(Check if the Bitmap is already in the cache)
String bitmapId = "" + category + "." + position;
mBitmap = sBitmapCache.get(bitmapId);
if (mBitmap == null) {
// 不在cache中,因此加载Bitmap并将其加入到cache。(It's not in the cache, so load the Bitmap and add it to the cache.)
//注意!我们向cache中添加了对象,但没有移除任何东西( DANGER! We add items to this cache without ever removing any.)
mBitmap = Directory.getCategory(category).getEntry(position)
.getBitmap(getResources());
sBitmapCache.put(bitmapId, mBitmap);
}
((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
}
我们在这里如意引入了一个内存泄漏:添加Bitmap到cache但没有移除任何对象。在一个真实的APP中,我们可能会限制缓存的大小。
用DDMS检查heap
The Dalvik Debug Moniter Server(DDMS)是初级的Android调试工具之一,是ADT插件的一部分,独立版本可以在SDK的tools/ directory目录找到。更多关于DDMS的信息,见UsingDDMS。
让我们用DDMS来检查这个APP的heap。你可以通过以下两种方式启动DDMS:
1、from Eclipse: click Window > Open Perspective > Other... >DDMS
2、from the commandline: run ddms (or ./ddms on Mac/Linux) in the tools/ directory
①在左边面板上选中om.example.android.hcgallery进程,然后②点击工具栏上的“show heap updates”按钮。然后③切换到DDMS中的“VM Heap”标签。它显示了每一次GC后,程序内存使用的一些基本的统计信息。看第一个更新,点击“Cause GC”按钮。
创建一个Heap Dump
让我们用Heap Dump来跟踪这个问题。①点击DDMS工具条上的“Dump HPROF file”按钮,选择你想存放文件的位置,然后②运行“hprof-conv”。在这个例子中,我将使用基础版本的MAT(version 1.0.1)。MAT download site
如果你正在运行ADT,且它安装了MAT插件,③点击“Dump hprof”按钮,会自动转换(执行hprof-conv),同时自动在Eclipse中打开转换后的hprof文件(用MAT打开)。
用MAT工具分析Heap dumps
①启动MAT,加载转换好的hprof文件。MAT是一个强大的工具,解释它的功能超出了本文的范围。因此我只要告诉一个你可以用来检测泄露的方法——直方图。
直方图显示了一个按照实例(instance)数量排序的列表,“浅堆(shallow heap)----所有实例使用的内存总量”或者“保留堆(retained heap)---所有实例的保留的内存总量,包括他们引用的其他对象”。
如果我们按照浅堆排序,我们能看到byte[]的实例在顶部。在Android3.0,Bitmap的像素数据对象被存在byte数组中(先前它没有存在Dalvik堆中),并根据这些对象的大小,可以肯定,Bitmap导致内存泄露了。(根据上面的图说的,不是所有的Bitmap都会导致内存泄露)
- 右击这个byte[]——》选择“List Object”>“with incoming reference”。这可以列出heap中我们可以按照shallow heap用量排序的所有byte数组。
- 选取一个大的对象,深入(一层层展开)。他会向你显示对象的根据经——一直维持着这个对象的链。Lo and behold(你瞧!)他就是我们的Bitmap Cache!
MAT不能肯定地告诉我们它泄露了,因为他不知道是否需要这些对象——只有开发者知道。这个cache使用了相对应用其他地方来说较多的内存,所以我们考虑限制它的大小。
用MAT比较Heap dumps
当调试内存泄露时,在两个不同的时间点比较Heap信息是很有用的。这样做,你需要创建两个独立的hprof文件(不要忘记用hpfor-conv工具转换他们)。
以下是你如何在MAT比较两个Heap dumps(有一点点复杂):
1、打开第一个HPROF文件(usingFile > Open Heap Dump).
2、打开柱状图。
3、在导航历史视图(use Window>Navigation History 如果它不可见),在主张图上右击,选择“Add to Compare Basket”。
4、打开第二个HPROF文件并重复步骤2和步骤3.
5、切换到“Compare Basket view”,点击“Compare the Results”)(顶部角落的红色“!”图标)
结论
在这篇文章中,我展示了Allocation Tracker和Heap dumps如何能让你的应用程序安全的使用内存。也演示了MAT检查你APP中的内存泄露。MAT是一个强大的工具,我只向你展示了冰山一角。如果你想了解更多,推荐你读一些文章:
- Memory Analyzer News:Eclipse MAT项目的官方博客
- 马库斯·科勒的Java性能博客有很多有用的文章,包括《用MAT分析Android应用程序内存使用》和《十条有用的MAT使用提示》。