Android内存泄漏监测(MAT)及解决办法

http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html

这篇文章是2010年1月份写的,其中有些已经不适合现在的Android机制了

我将内存问题分为两种:OOM和堆栈溢出 
一个Android进程可以分配的最大堆内存(heap memory)为 16M(现在各个定制版本的Android系统都不一样)

如果你将重复打开关闭一个Activity20次,就有可能出现内存溢出,那么在哪里内存溢出了,在哪里可以被GC回收?

adb shell procrank 
使用这个命令可以获得一些应用的内存数据,但是只能获取很简单的数据

adb shell dumpsys meminfo 
使用这个命令可以得到更多相关数据

如果想要了解更多关于内存分配和回收的细节就需要 Eclipse Memory Analyzer Tool (MAT)工具。 
MAT工具使用方法:

http://kohlerm.blogspot.com/2009/04/analyzing-memory-usage-off-your-android.html 
http://blog.csdn.net/sgwhp/article/details/9842509

MAT可以显示的内容

最佳实践:

1.在onDestroy()中最好将匿名listeners都设置为null,比如view.setOnClickListener(null) ;

2.即使没有静态drawable变量,但drawable对象中引用了view,view对象引用了Context,所以Activity可能会泄漏;

//Android源码片段   Drawable对象中引用View对象

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
public void setBackgroundDrawable(Drawable background) {
.....
background.setCallback(this);
......
}
} 

public abstract class Drawable {
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
} 

所以最好在 onDestroy() 中调用view.getBackground().setCallback(null); 
在stackoverflow的相关问答中出现了通用的unbindDrawables方法 
http://stackoverflow.com/questions/9461364/exception-in-unbinddrawables

@Override
protected void onDestroy()
{
super.onDestroy();
unbindDrawables(findViewById(R.id.top_layout));
System.gc();
Drawables(View view)
{
if (view.getBackground() != null)
{
view.getBackground().setCallback(null);
view.setBackground(null); //添加
}
if (view instanceof ViewGroup && !(view instanceof AdapterView))
{
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
{
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
} 

【补充】上述方法对ListView和GridView无用

3. 在onDestroy()中设置object = null 虽然设置object = null不是必要的,但是可以让GC加快一点;

4.让所有对象设置为null,虽然是很方便,但是也容易出错,最优的方法是通过MAT工具来监测哪些对象可能会泄漏,然后在 onDestroy()中置null。不然的话如果一个后台线程还在运行并且需要调用这些对象,那么就容易出现NullPointerExceptions。

5.Images(Bitmaps)不像一般Java对象一样分配内存而是通过调用natvie方法,所以bitmap占用了非Dalvik堆的内存。所以使用MAT工具时会发现用procrank和dumpsys命令查看的内存会比MAT中的大很多。 
【补充】 
 摘自https://developer.android.com/training/displaying-bitmaps/manage-memory.html (翻译:) http://su1216.iteye.com/blog/1931629


(1) Android2.2及之前版本,GC时应用的线程都会停止,这样会引起延迟,导致性能降低。所以在2.3中添加了并发GC机制,所以只要bitmap不再被引用,所占内存就马上会被回收。

(2) 在2.3及之前版本,bitmap的像素数据是存储在native memory中的,并不是和bitmap对象一起存储在Dalvik heap里的,会导致native memory中的像素数据不能被显式回收,造成应用超出内存大小限制而崩溃。 
在2.3 (API level 11)之后的版本,像素数据就和bitmap对象一起存储在Dalvik heap上了。

6.加载网络如片在Android中会比较难以操作,只是加载几个200x200像素的图片就会导致内存占用的暴涨,20k大小的200x200的png图片需要占用160K左右的内存(每个像素占用4bytes)[200x200x4/1024 = 156.25k ]

推荐使用下列方法【摘自 http://android-developers.blogspot.com/2008/09/android-photostream.html】 
(1) 首先获得图片的的大小(像素)

BitmapFactory.Options options = new BitmapFactory.Options();
//只加载图片的部分信息,减少内存占用
options.inJustDecodeBounds = true;
Bitmap tmpBitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(new URL(url).openStream()), null, options);
//获取图片的长宽像素
int height = options.outHeight;
int width = options.outWidth; 

(2) 这样就在下载网络图片之前就可以知道图片的大小了,然后根据最终需要显示的图片大小进行压缩(通常Android会在绘制的时候缩放图片)

options = new BitmapFactory.Options();
options.inSampleSize = sampleSize;//压缩比列,如果是3,就会压缩到1/3
bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(new URL(url).openStream()), null, options); 

这样获取的图片内存占用会小一点。

(3) 更好的办法是将图片下载到本地,在使用的时候根据大小重新缩放。

将不再需要图片对象时调用bitmap.recyce()来释放掉图片的内存占用,但是如果你调用了recycle(),之后又试图绘制这个bitmap,你会得到 错误:“Canvas: trying to use a recycled bitmap”(适合Android2.3及之前版本)。

7.ListView的convertView缓存方式 【网上资料很多】

8.debug的时候会保持对象处于可用状态,内存不能被回收。所以内存分析(使用MAT工具)的时候不要使用debug。并且在heap dump之前多进行几次GC操作

【原因:http://groups.google.com/group/android-developers/browse_thread/thread/7b0ea57d9507d33f

文章的回复里有个问题 
问题: 
我写了一个简单的demo,只有两个activity,重复打开SecondActivity 6次,使用MAT工具查看内存发现有6个SecondActivity对象,为什么会这样? 
回答: 
可能导致上述问题的原因 
(1) 怎么打开和关闭SecondActivity的,是通过Intent吗?是通过硬件返回按钮关闭SecondActivity的吗? 
(2) 在SecondActivity中重载onDestroy(),打上log,重复打开SecondActivity 6次,onDestroy()中的log会打印6次吗? 
(3) 用多快速度重新打开和关闭SecondActivity的?最好是当看见一个GC log时再重新打开,可能是还没有来得及GC,所以内存中会有多个对象(可有可能是你的demo内存占用太小,没有达到需要GC的条件) 
(4)检查一下6个SecondActivity对象的状态是否为unknown,MAT也会统计unkown状态的对象。【补充:可以使用adb shell dumpsys meminfo 命令查看当前内存中Activity存在的数目】

时间: 2024-11-05 19:01:25

Android内存泄漏监测(MAT)及解决办法的相关文章

前端知识体系:JavaScript基础-作用域和闭包-闭包的实现原理和作用以及堆栈溢出和内存泄漏原理和相应解决办法

闭包的实现原理和作用 闭包: 有权访问另一个函数作用域中的变量的函数. 创建闭包的常见方式就是,在一个函数中创建另一个函数. 闭包的作用: 访问函数内部变量.保持函数在环境中一直存在,不会被垃圾回收机制处理 因为函数内部声明 的变量是局部的,只能在函数内部访问到,但是函数外部的变量是对函数内部可见的,这就是作用域链的特点了. 子级可以向父级查找变量,逐级查找,找到为止 因此我们可以在函数内部再创建一个函数,这样对内部的函数来说,外层函数的变量都是可见的,然后我们就可以访问到他的变量了. <scr

Android内存泄漏检测与MAT使用

内存泄漏基本概念 内存检测这部分,相关的知识有JVM虚拟机垃圾收集机制,类加载机制,内存模型等.编写没有内存泄漏的程序,对提高程序稳定性,提高用户体验具有重要的意义.因此,学习java利用java编写程序的时候,要特别注意内存泄漏相关的问题.虽然JVM提供了自动垃圾回收机制,但是还是有很多情况会导致内存泄漏. 内存泄漏主要原因就是一个生命周期长的对象,持有了一个生命周期短的对象的引用.这样,会导致短的对象在该回收时候无法被回收.Android中比较典型的有:1.静态变量持有Activity的co

Android内存泄漏查找和解决

Android内存泄漏查找和解决 目录: 内存泄漏的概念 一个内存泄漏的例子 Java中"失效"的private修饰符 回头看内存泄漏例子泄漏的重点 强引用与弱引用 解决内部类的内存泄漏 Context造成的泄漏 使用LeakCanary工具查找内存泄漏 总结 一.内存泄漏概念 1.什么是内存泄漏? 用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元.直到程序结束.即所谓的内存泄漏. 其实说白了就是该内存空间使用完毕之后未回收 2.内存泄漏会导致的问题 内

利用 LeakCanary 来检查 Android 内存泄漏

前言 你被概率性的 OOM 困扰么?有时候,OOM 像幽灵一样,挥之不去,可真想把它揪出来时,又捉之不着.或许,是时候用 LeakCanary 来诊断一下了.它是一个用来检查 Android 下内存泄漏的开源库,这篇文章主要介绍其用法.架构和其背后的实现原理. Square 有篇文章介绍了开发这个库的原因.他们的一个付款流程里,需要用到用户的签名,他们直接用 Bitmap 来画签名,Bitmap 大小和屏幕分辨率是一样的.问题来了,在试图创建这个 Bitmap 对象时,概率性 OOM 如幽灵般相

Android 内存泄漏

在分享会上听小伙伴对这部分内容做了讲解,发觉在平时的编程中确实有很多问题没有注意到,故记录下来分享给各位,也欢迎各位不吝赐教纠正文中不足之处. 内存泄漏与内存溢出: 内存溢出简单讲就是程序运行要求的内存大于虚拟机能提供的最大内存,会导致程序崩溃,也就是我们常见的Out Of Memory错误. 内存泄露指程序未能释放已经不再使用的内存.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于程序设计的失误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费.少量的内存

Java 内存泄漏--全解析和处理办法 [ 转载 ]

Java内存泄露——全解析和处理办法 [转载] @author 小筐子 @address http://www.jianshu.com/p/bf159a9c391a 本文章会一步一步的探讨内存泄露的问题.博主第一次书写长篇技术贴,如有错误或不周到的地方请多指教. JAVA是垃圾回收语言的一种,开发者无需特意管理内存分配.但是JAVA中还是存在着许多内存泄露的可能性,如果不好好处理内存泄露,会导致APP内存单元无法释放被浪费掉,最终导致内存全部占据堆栈(heap)挤爆进而程序崩溃. 内存泄露 说到

android 内存泄漏分析技巧

java虚拟机运行一般都有一个内存界限,超过这个界限,就会报outofmemory.这个时候一般都是存在内存泄漏.解决内存泄漏问题,窃以为分为两个步骤:分析应用程序是否真的有内存泄漏,找到内存泄漏的地方.这两个步骤都不是一般意义上的调试,直接打log,断点调试都不是太给力.动脑筋想一想,内存问题应该在很多地方上都会出现,这么常见的问题应该是有工具的.android现在更可以说是一个生态系统,当然也有很多开发辅助工具.在前面的两个步骤中都有很强大的武器,熟练的掌握这些利器,分析问题就会事半功倍.

Android —— 内存泄漏检查

今天地铁上看到一篇不错的将内存泄漏简单检查的文章,觉得还不错哟,内存泄漏确实是每个程序员头疼的事情,这里就多研究一下咯^^ 一. 常见的垃圾回收算法 参看文章 引用计数法 引用计数法基本上最简单的垃圾回收策略,它的核心思想是: 当有指针指向某实例时,计数加一, 当删除一个指针时,计数减一,当计数为0时,说明该实例没有引用可以被垃圾回收器回收. 这种回收策略的缺点是显而易见的: 1.维护引用计数是有开销的 2.计数的保存会消耗额外的空间 3.无法处理循环引用 标记清除 标记清除,顾名思义分为2步:

android 内存泄漏检测工具 LeakCanary 泄漏金丝雀

韩梦飞沙 yue31313 韩亚飞 han_meng_fei_sha [email protected] 内存泄漏检测工具 android 内存泄漏检测工具 ======== 内存泄漏 就是  无用的对象没有被回收,占用着内存,使得可用内存变小了. 如何检测内存泄漏, 可以使用 LeakCanary来检测内存泄漏. leak  是 泄漏的意思.. Canary 是 金丝雀 的意思. 在运行 应用的时候, 泄漏金丝雀 如果检测到内存泄漏 会显示一个通知. ======== LeakCanary捕获