Android应用程序内存分析

原文链接:http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html

Dalvik 自带有内存回收机制,但这并不意味程序员可以忽略内存的管理。在移动设备上,内存比较紧缺,因此你应当对内存的使用更加小心。在本文中,我们将介绍如何使用android SDK中的内存分析工具来对应用程序的内存使用情况进行分析。

有些内存问题是显而易见的。例如,如果应用程序在每次用户触摸屏幕时都会泄漏一些内存,那么该应用将很快因为OutOfMemoryError的异常而崩溃。另外一些则更加隐蔽,它们有可能只会导致应用程序和整个系统变慢(垃圾回收器频繁和长时间进行垃圾回收)

调试工具

在android SDK中提供了两种内存分析的方法:内存申请跟踪(以下用Allocation tracker) 和 堆转储(以下用heap dump)。Allocation tracker可以帮我们了解到一段时间内,应用对内存的申请情况,但是它不能提供任何有关应用程序堆的信息。更多有关内存申请追踪的信息请看Tracking Memory Allocations。接下来的篇幅中我们将重点关注一种更加强有力的分析工具——heap dump的使用。

Heap dump是应用程序堆的的快照,它存放在一种特定格式(HPROF)的二进制文件中。Dalvik使用类似于HPROF的格式来保存heap dump。生成应用程序的heap dump的方法有多种,DDMS中的head dump按钮就提供了生成heap dump的功能。如果想要对heap dump的生成有更加精细的控制,可以在程序中使用android.os.Debug.dumpHprofData()来生成。

常用的分析heap dump的工具有jhat 和Eclipse Memory Analyzer (MAT)。在分析之前,我们需要将Dalivk heap dump文件的格式转换成J2SE 标准的HPROF格式。android SDK中提供了hprof-conv工具来进行这个转换。命令如下:

        hprof-conv dump.hprof converted-dump.hprof

例子:如何分析内存泄漏

在Dalivk环境中,程序员不能显式的申请和释放内存,因此内存不会像在C/C++中的那样真正泄漏。在这种情况下,内存泄漏往往指的是程序中保存了无用的对象的引用,有时保存一个对象的引用,而这个对象又拥有大量其他对象的引用,那么这些对象将都无法被垃圾回收器回收。

让我们看android SDK中的一个示例程序Honeycomb Gallery sample app。这是一个用来展示如何使用android Honeycomb 中的新API的照片库应用(如何下载和编译这个应用的代码,请看介绍)。我们将小心的在这个应用的代码中加入一个内存泄漏的bug,以讲述如何调试这样的问题。

假如我们想修改这个应用,让其具有从网络上下载图片的能力。为了提升体验,我们决定实现一个缓存以存储最近收到的图片,我们通过对ContentFragment.java文件进行一些小的修改来实现这个想法。首先在这个类的顶部加上一个静态的变量

private static HashMap<String,Bitmap> sBitmapCache = new HashMap<String,Bitmap>();

这个变量将保存我们想要缓存的图片,现在我们修改updateContentAndRecycleBitmap() 加入检查存储和存入缓存的功能

void updateContentAndRecycleBitmap(int category, int position) {    if (mCurrentActionMode != null) {        mCurrentActionMode.finish();    }     // Get the bitmap that needs to be drawn and update the ImageView.     // Check if the Bitmap is already in the cache    String bitmapId = "" + category + "." + position;    mBitmap = sBitmapCache.get(bitmapId);     if (mBitmap == null) {        // It's not in the cache, so load the Bitmap and add it to the 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);}

这里,我故意留下了一个memory leak的问题:我们将Bitmap添加到cache但是却从来不从cache中删除它们。在真正的应用中,往往会对cache的大小进行一个限制。

使用DDMS来分析内存使用情况

DDMS是android最主要的一个调试工具,它是ADT插件的一部分,同时在android SDK的tools/ directory目录下也有一个独立的版本。更对关于DDMS的信息,请看Using DDMS

让我们使用DDMS来分析内存使用情况。DDMS有两种启动方式:

  • 从Eclipse:点击Window > Open Perspective > Other... > DDMS
  • 从命令行:在tools/ directory中执行ddms命令
Android应用程序内存分析-Memory Analysis for Android Applications_第1张图片

在左边的面板里选择进程com.example.android.hcgallery,然后点击工具栏上的Show heap updates按钮,之后切换到DDMS中的VM Heap页。这里显示了堆内存的基本状态信息,这些信息每次GC时更新。点击Cause GC按钮可以触发第一次更新。
Android应用程序内存分析-Memory Analysis for Android Applications_第2张图片

可以看到,我们使用了8MB多一点的内存,现在滑动相册,看到这个数字会变大。由于应用中总共只有13張图片,因此我们不会无限制的泄漏内存。从某种意义上来说,这是一种最糟糕的泄漏,因为我们永远也不会收到OutOfMemoryError的异常来帮助我们发现这个问题。
创建一个heap dump
让我们使用heap dump来定位这个问题。点击中DDMS工具栏上的Dump HPROF按钮,选择一个保存路径,然后运行hprof-conv处理这个文件。在这个例子中,我将使用MAT的独立应用版本(版本号1.0.1),下载链接MAT download site 如果你正在使用ADT并且Eclipse中已经安装了MAT插件,点击dump HPROF按钮,工具将会自动对HPROF文件进行格式转换,然后直接在Eclipse中使用MAT打开转换后的HPROF文件。
使用MAT分析heap dump
启动MAT,打开我们转换过的HPROF文件。MAT是个强大的工具,拥有很多功能。在这篇文章中,我只介绍一种使用它来分析内存问题的方法:Histogram view。Histogram view显示了堆中所有类的列表,我们可以将列表中这些类按照其实例(object)的个数的多少排序(包括按照浅堆中实例的个数和深堆中实例的个数)
Android应用程序内存分析-Memory Analysis for Android Applications_第3张图片

如果我们按照浅堆排序,我们可以看到byte[]类型排在第一位,这是因为在android3.0中bitmap的像素点数据都保存在byte数组中(之前它们都没有被保存在dalik的堆空间中),基于这些数组的大小,我们可以断言,这些数组就是我们泄漏的bitmap内存的一部分。 在列表中右击byte[]类型,在弹出菜单中依次选择List Objects > with incoming references,可以看到,在之前视图中我们点击的类型对应的所有浅堆中的byte array对象都被列了出来。 选择其中一个比较大的对象,点击箭头查看下拉列表中的内容,这里我们可以看到这个对象的整条引用链,其中可以找到我们的bitmap cache。 Android应用程序内存分析-Memory Analysis for Android Applications_第4张图片
MAT不能明确的告诉我们哪里发生了泄漏,因为它并不知道这些对象是否还在使用——仅仅只有程序员知道这一点。在这个例子中,缓存占用了应用程序大量的内存,因此在这种情况下,我们应该考虑对cache的大小进行限制。
使用MAT对heap dump进行比较
有时比较两个不同时间点的heap dump对我们分析内存泄漏问题是很有帮助的。首先,我们需要生成两个heap dump文件(别忘记使用 hprof-conv进行转换)
接下来我们可以使用MAT进行比较了(过程稍稍有些复杂)
  • 打开第一个HPROF 文件(使用 File > Open Heap Dump)
  • 打开Histogram view
  • 在Navigation History view中(如果找不到使用 Window > Navigation History打开), 对其中的histogram条目右击,然后选择Add to Compare Basket.
  • 重复2~3步骤,打开第二个HPROF文件
  • 切换到Compare Basket 页,点击Compare the Results按钮(这页右上角的红色按钮)
总结
在本文中,我展示了如何通过Allocation Tracker 和 heap dumps 这两个工具来 更好的认识 应用内存的使用情况有。我也展示了如何使用MAT来追踪应用中的内存泄漏问题。MAT是个强大的工具,而我在这里仅仅只是提到了它的皮毛。如果你想要了解更多相关内容,我建议你阅读下面的文章
  • Memory Analyzer News: Eclipse MAT官方blog
  • Markus Kohler 的java性能分析博客有很多有用的文章, 包括 Analysing the Memory Usage of Android Applications with the Eclipse Memory Analyzer 和10 Useful Tips for the Eclipse Memory Analyzer.

更多相关文章

  1. ADB 工具
  2. 【拿来主义】Android反编译工具
  3. 面试例题1:如何在android应用程序的窗口上绘制图形
  4. android保存文件到手机内存
  5. Android文档阅读03—开发工具
  6. Android 基础 源码 工具
  7. Android SDK 21.1 发布,Android 开发工具包
  8. 用Eclipse开发第一个Android应用程序HelloWorld
  9. Google正式推出Android 3.2开发工具

随机推荐

  1. android学习笔记之--android中各种java包
  2. android Menu 菜单使用总结
  3. 可循环显示图像的Android Gallery组件
  4. android页面切换动画
  5. Android 跨进程SharedPreferences异常详
  6. Android 使用MediaPlayer播放assets目录
  7. android 打开app先显示欢迎界面后自动跳
  8. Android R system_ext动态扩展分区
  9. android 4.0 browser useragent debug
  10. android p 4G LTE 默认关闭修改