Android 官方博客 - Android应用程序的内存分析(翻译)(转)

转自:http://www.cnblogs.com/wisekingokok/archive/2011/11/30/2245790.html

Dalvik虚拟机支持垃圾收集,但是这不意味着你可以不用关心内存管理。你应该格外注意移动设备的内存使用,在上面内存空间是受到限制的。在这篇 文章里面,我们来看看Android SDK里面的一些内存剖析工具(profiling tools)是如何帮助我们修整应用程序的内存使用。

一些内存使用问题是很明显的,例如,如果在每次用户触摸屏幕的时候应用程序有内存泄露,将会有可能触发OutOfMemoryError,最终程序崩溃。另外一些问题却很微妙,也许只是降低应用程序和整个系统的性能(当高频率和长时间地运行垃圾收集器的时候)。

必要的工具:

Android SDK提供了2个主要的剖析应用程序内存使用情况的工具:DDMS里的一个分页Allocation Tracker和heap dumps。Allocation Tracker是很有用的,特别是当你想得到程序在一定的时间里内存的分配情况的一种感性认识的时候。但是它不能给你任何关于程序heap总体情况的任何 信息。关于Allocation Tracker的更多信息,请看文章Tracking Memory Allocations 。文章剩下的内容将把重点放在heap dumps,它是更强大的内存分析工具。

一个heap dump就是一个程序heap的快照,它保存为一种叫做HPROF的二进制格式。Dalvik用的也是类似的格式,但是不完全一样。这里是Java的HPROF工具 。有很多方法去生成一个运行时应用程序的heap dump。其中一种就是使用在DDMS里边的Dump HPROF file按钮。如果想产生更精确的dump数据,可以在程序中使用android.os.Debug.dumpHprofData() 方法。

分析heap dump,你可以使用一些标准的工具比如jhat 或者Eclipse Memory Analyzer(MAT) 。不过,首先你需要把.hprof文件从Dalvik格式转换成J2SE HPROF格式。你可以使用Android SDK提供的hprof-conv工具。例如:

  1. hprof-conv dump.hprof converted-dump.hprof

调试一个内存泄露实例:

在Dalvik运行时里边,程序员不能显式地分配和释放内存,所以这里的内存泄露跟c和c++里面的不同。在你的代码里边,内存泄露就是你保留了一个并不再需要的类对象的引用。有时候仅仅一个引用就会阻碍gc对一大堆对象的回收。

我们来过一个实际的例子,Android SDK里面提供的范例程序Honeycomb Gallery sample app 。它是一个photo gallery程序,用来演示一些新的Honeycomb API的使用。(下载和编译这些代码,请看这些命令 。)我们会有意地加入一个内存泄露在程序里边,然后来演示如何调试它。

想象一下我们想修改程序让它从网络下载图片。为了让它更具备灵活性,我们可以考虑实现一个缓存,保存最近查看过的图片。我们可以对ContentFragment.java做一些小的修改来达到这个目的。在class顶部,我们增加一个新的静态变量:

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

这里就是我们保存缓存的地方。现在我们可以修改updateContentAndRecycleBitmap()方法,让它在下载之前先查看是否数据已经存在,如果不存在就去下载,然后添加数据到缓存。

  1. void updateContentAndRecycleBitmap(int category, int position) {
  2. if (mCurrentActionMode != null) {
  3. mCurrentActionMode.finish();
  4. }
  5. // Get the bitmap that needs to be drawn and update the ImageView.
  6. // Check if the Bitmap is already in the cache
  7. String bitmapId = "" + category + "." + position;
  8. mBitmap = sBitmapCache.get(bitmapId);
  9. if (mBitmap == null) {
  10. // It‘s not in the cache, so load the Bitmap and add it to the cache.
  11. // DANGER! We add items to this cache without ever removing any.
  12. mBitmap = Directory.getCategory(category).getEntry(position)
  13. .getBitmap(getResources());
  14. sBitmapCache.put(bitmapId, mBitmap);
  15. }
  16. ((ImageView) getView().findViewById(R.id.image)).setImageBitmap(mBitmap);
  17. }

我已经在这里故意引入了一个内存泄露的问题:我们把图片加入了缓存但是从来没有移除他们。在真实的应用里,我们可以会用某种方法来限制缓存的大小。

在DDMS里检查heap的使用情况

Dalvik Debug Monitor Server(DDMS)是主要的Android调试工具之一,也是ADT Eclipse plug-in 的一部分,独立的程序版本也可以在Android SDK的根目录下的tools/下面找到。关于DDMS更多的信息,请参考使用DDMS 。

我们来使用DDMS检查这个应用的heap使用情况。你可以使用下面的两种方法启动DDMS:

  • from Eclipse: click Window > Open Perspective > Other... > DDMS
  • or from the command line: run ddms (or ./ddms on Mac/Linux) in the tools/ directory

在左边的面板选择进程com.example.android.hcgallery,然后在 工具条上边点击Show heap updates按钮。这个时候切换到DDMS的VM Heap分页。它会显示每次gc后heap内存的一些基本数据。要看第一次gc后的数据内容,点击Cause GC按钮:

我们可以看到现在的值(Allocated列)是有一些超过8MB。现在滑动相片,这时看到 数据在增大。因为只有仅仅13个相片在程序里边,所以泄露的内存只有这么大。在某种程度上来说,这时最坏的一种内存泄露,因为我们没法得到 OutOfMemoryError来提醒我们说现在内存溢出了。

生成heap dump

我们现在使用heap dump来追踪这个问题。点击DDMS工具条上面的Dump HPROF文件按钮,选择文件存储位置,然后在运行hprof-conv。在这个例子里我们使用独立的MAT版本(版本1.0.1),从MAT站点下载 。

如果你使用ADT(它包含DDMS的插件)同时也在eclipse里面安装了MAT,点击“dump HPROF”按钮将会自动地做转换(用hprof-conv)同时会在eclipse里面打开转换后的hprof文件(它其实用MAT打开)。

用MAT分析heap dumps

启动MAT然后加载刚才我们生成的HPROF文件。MAT是一个强大的工具,讲述它所有的特性超出了本文的范围,所以我只想演示一种你可以用来检测 泄露的方法:直方图(Histogram)视图。它显示了一个可以排序的类实例的列表,内容包括:shallow heap(所有实例的内存使用总和),或者retained heap(所有类实例被分配的内存总和,里面也包括他们所有引用的对象)。

如果我们按照shallow heap排序,我们可以看到byte[]实例在顶端。自从Android3.0(Honeycomb),Bitmap的像素数据被存储在byte数组里 (之前是被存储在Dalvik的heap里),所以基于这个对象的大小来判断,不用说它一定是我们泄露掉的bitmap。

右击byte[]类然后选择List Objects > with incoming references。它会生成一个heap上的所有byte数组的列表,在列表里,我们可以按照Shallow Heap的使用情况来排序。

选择并展开一个比较大的对象,它将展示从根到这个对象的路径--就是一条保证对象有效的链条。注意看,这个就是我们的bitmap缓存!

MAT不会明确告诉我们这就是泄露,因为它也不知道这个东西是不是程序还需要的,只有程序员知道。在这个案例里面,缓存使用的大量的内存会影响到后面的应用程序,所以我们可以考虑限制缓存的大小。

使用MAT比较heap dumps

调试内存泄露时,有时候适时比较2个地方的heap状态是很有用的。这时你就需要生成2个单独的HPROF文件(不要忘了转换格式)。下面是一些关于如何在MAT里比较2个heap dumps的内容(有一点复杂):

  1. 第一个HPROF 文件(using File > Open Heap Dump ).
  2. 打开 Histogram view.
  3. 在Navigation History view里 (如果看不到就从Window > Navigation History找 ), 右击histogram 然后选择Add to Compare Basket .
  4. 打开第二个HPROF 文件然后重做步骤2和3.
  5. 切换到Compare Basket view, 然后点击Compare the Results (视图右上角的红色"!"图标)。

总结

这本篇文章里面,我展示了Allocation Tracker和heap dumps是如何给你一种对程序内存使用的感性认识。我也展示了Eclipse Memory Analyzer(MAT)可以帮助追逐我们程序里面的内存泄露问题。MAT是一个强大的工具,我也仅仅触碰了一些皮毛,如果你想学习更多内容,我建议读 一些下面的文章:

时间: 2024-10-11 21:49:56

Android 官方博客 - Android应用程序的内存分析(翻译)(转)的相关文章

欢迎加入51CTO官方博客QQ群112163514

大家好,我是51CTO博客小管家--奋斗的小歌.来到51CTO这个大家庭已经两周时间了,认识了不少IT技术达人,你们在我心中真的是棒棒哒! 感谢小伙伴们对51CTO技术博客的支持,特此开放51CTO官方博客QQ群112163514,这里会定期发布51CTO博客最新公告.活动及动态. 如果你喜欢IT技术,喜欢探讨技术问题,喜欢交流学习经验,那加入我们一定没错.这里拥有大批IT技术人才和技术文章,精彩好玩的活动,这里,是你找到志同道合朋友的乐园! 还等什么,赶快加入我们的51CTO官方博客QQ群,踏

腾讯DBA官方博客开通了,欢迎交流

腾讯DBA官方博客开通了,欢迎交流哈..麻烦给放到首页一下 http://tencentdba.com 腾讯互娱游戏DBA团队一直致力于为游戏提供稳定.高效的DB运营服务,这是我们团队的使命. 过去DBA团队一直专注于业务本身,没有太多精力将我们的技术及解决方案对外输出,因此,对于开通团队Blog的事宜,之前的筹划被无限期搁置... 2015新年伊始,我们终于将这个事情提上日程!新的起点,我们愿意将过去积累的经验及教训,及未来的成长及进步点滴,都记录下来,与大家同分享.共成长. 小伙伴们,一起加

【官方博客】Android’s HTTP Clients

Android’s HTTP Clients [This post is by Jesse Wilson from the Dalvik team. —Tim Bray] Most network-connected Android apps will use HTTP to send and receive data. Android includes two HTTP clients: HttpURLConnection and Apache HTTP Client. Both suppor

【官方博客】StrictMode API for Built-In Performance Monitoring

http://android-developers.blogspot.com/2010/12/new-gingerbread-api-strictmode.html StrictMode API for Built-In Performance Monitoring [This post is by Brad Fitzpatrick, an Android Software Engineer who worries unreasonably about responsiveness. —Tim

Dropbox 同步的工作方式,每个文件被分成4M的小块(官方博客)

http://www.zhihu.com/question/41544586 https://blogs.dropbox.com/tech/2014/07/streaming-file-synchronization/

div水平居中与垂直居中的方法【摘自美浩工作室官方博客 】

大家往往在写页面中会遇到不固定宽和高的div如果水平和垂直都居中呢?在写css的时候经常遇到的一个问题,当div没有固定的宽度或者高度的时候,如何才能让div水平或者垂直居中显示.如果div有固定宽度的话,用padding,margin都很容易实现.方法有很多种.不过经常遇到这种div没有固定的宽度高度的情况,我们就不能用margin,padding设置固定的距离了.这个问题让很多人头疼.而怎么样才能让这个div居中显示呢?其实这种情况解决的办法也是有很多种,js,css都可以实现. 1.先来看

如何用MAT分析Android程序的内存泄露

本文结合<Android开发艺术探索>书籍中的内存分析例子来讲解如何利用MAT工具来查找内存泄漏(以AndroidStudio开发工具为例). 1.下载MAT(Eclipse Memory Analyzer)工具,windows64位网盘下载地址:http://pan.baidu.com/s/1pLlbOBD,或者通过官网下载:https://www.eclipse.org/mat/downloads.php,下载完毕后解压即可,目录结构如下: 2.模拟内存泄漏的场景,源码如下,启动退出三次a

Android开发周报:Android L默认加密用户数据

Android开发周报:Android L默认加密用户数据 新闻 <iCloud前车之鉴,Android L默认开启加密功能>:iCloud 艳照风波再起,第二波女星照片流出,大量女星的裸照又开始在社交网站疯传,本次大规模的“艳照门”依然有可能是黑客利用苹果iCloud云端系统的漏洞, 在破解了女艺人们所设的简单密码后侵入并非法盗取了裸照,继而在网络论坛发布.注重保护用户的隐私,是厂商们义不容辞的责任,谷歌宣布Android L上将默认加密用户数据. <苹果出了个指南教你怎么从Andro

Java程序开发中的简单内存分析

首先说明内存总体分为了4个部分, 包括 1.stack segment (栈区存储基本数据类型的局部变量,对象的引用名) 2.heap segment(堆区,一般用于存储java中new 出来的对象) 3.code segment (代码段) 4.data segment (数据段,静态数据常量) 其中我们程序中用关键字new出来的东西都是存放在heap segment: 程序中的局部变量存放在stack segment,这些局部变量是在具体方法执行结束之后,系统自动释放内存资源(而heap s