转自:http://blog.csdn.net/ztp800201/article/details/16830191
今天在项目中碰到了史无前例的内存泄漏问题,在大量使用Bitmap的Activity的中概率性出现如下错误:
07-13 13:17:20.534: ERROR/AndroidRuntime(5161): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
也就是OOM内存溢出的问题。
在 Java中,JavaVM拥有自动管理内存的功能,其中就有鼎鼎大名的System.GC();这个东东。Java的GC能够进行垃圾回收,但是这不是 C/C++,你并不知道它什么时候能够回收,甚至你主动告诉VM“我要释放这块内存”也无济于事,因为它也就是无精打采的告诉你“知道了”。仅次而已。不 要迷信System.GC(),GC真的是个传说。
话又说过来了,那有自动回收这个功能就没有什么必要了,反正又不听使唤。这就是Java高级的一个体现,它不会让你有权利操作内存的,它认为这是危险的操作。所以Java写久了的人就会忘记了C++中这个东东:
delete
对,这个关键字在Java中已经不存在了,所以你就会发现new成了孤家寡人。
我们经常看到Java中的这段代码:
Object mObject = new Object();
但是很多时候出现下面代码的机会要远远小于上面:
mObject = null;
其 实这像是个废话,因为对象不用了,自然会被VM回收掉,而且mObject = null;这句话并不会让其内存空间立即释放,所以也就没什么用了。其实不然,像Object mObject = new Object();这种构建新对象的方法叫做强引用(还有软引用、弱引用和薄引用,具体区别请百度),除非它不再被任何人引用,否则它始终存在内存中。实 际上置空这个操作就是在我们可控的范围内尽量减少对一个无用内存的引用数量,从而提高内存回收的优先级,换句话说,它越垃圾,越没用,越会被回收。
说了半天那么和ImageView有什么关系呢?
看如下代码:
ImageView mImageView = (ImageView) findViewById(R.id.imageView1) ;
Bitmap bitmap = Bitmap.createBitmap(BitmapFactory.decodeFile(“/sdcard/test.jpg”));
if(bitmap != null)
mImageView.setImageBitmap(bitmap);
相信用过ImageView的人都用过类似的用法,看上去没什么问题,实际上你会遇到一些麻烦。
我做了个试验,新建一个工程,仅有一个Activity,仅有一个ImageView,仅放入了一个Bitmap。
然后我启动应用,点击返回,再启动应用,再返回……等到了第五回,程序挂掉了,抛出的异常为:
ERROR/AndroidRuntime(5161): java.lang.OutOfMemoryError: bitmap size exceeds VM budget。
Oh Shit!这么简单的代码都会挂掉!
为此我看了一下setImageBitmap()这个方法的源码,发现实际上它内部做了类似指针传递的操作:
public void setImageBitmap(Bitmap bm) {
// if this is used frequently, may handle bitmaps explicitly
// to reduce the intermediate drawable object
setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}
public BitmapDrawable(Resources res, Bitmap bitmap) {
this(new BitmapState(bitmap), res);
mBitmapState.mTargetDensity = mTargetDensity;
}
BitmapState(Bitmap bitmap) {
mBitmap = bitmap;
}
看见了吧,它并没有新建Bitmap对象,而是对其进行了引用而已。
所以我们执行完setImageBitmap(bitmap)这个方法后应该执行bitmap = null这个操作,让我们这边的引用断掉。提高其被回收的可能性。随后执行System.gc()方法后,就会在程序销毁时bitmap的内存被释放。
不过我们不能随后执行Bitmap.recycle()这个方法,这会强制收回Bitmap的内存,这样就会让ImageView不会显示任何图片并且报错退出。
但是我们可以这么做,将Bitamp定义为类成员变量,然后重载Activity类的protected void onDestroy()方法,如下:
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if(!bitmap.isRecycled())
bitmap.recycle();
}
这样在程序退出后,ImageView中的Bitmap就会被回收掉,减少内存的浪费。
那究竟这两种方法哪个更好用呢?请持续关注我的试验。
本文属试验性研究,如有不同意见,请留言或联系作者本人:[email protected]
注:本文属hein@江南`忆原创,转载请注明出处http://www.jiangnane.com/