Android内存泄露和管理,Android内存管理及内存泄露

Android内存管理及内存泄露,最近在看Dalvik相关的东西,发现文章不少,但许多文章都拿java虚拟机来说Dalvik,但两者是有一些不同的。

33df588c17a841df93b704dfc0d91901.png

一、Dalvik的内存结构

和java虚拟机不同,Dalvik虚拟机使用的指令是基于寄存器的,而Java虚拟机使用的指令集是基于堆栈的。Dalvik虚拟机的内存大体上可以分为Java Object Heap、Bitmap Memory和Native Heap三种。

1.Java Object Heap

Java Object Heap的最小和最大默认值为2M和16M,但是手机在出厂时,厂商会根据手机的配置情况来对其进行调整,例如,G1、Droid、Nexus One和Xoom的Java Object Heap的最大值分别为16M、24M、32M 和48M。我们可以通过ActivityManager类的成员函数getMemoryClass来获得Dalvik虚拟机的Java Object Heap的最大值。

2.Bitmap Memory

Bitmap Memory也称为External Memory,它是用来处理图像的。在3.0之前,Bitmap Memory是在Native Heap中分配的,但是这部分内存同样计入Java Object Heap中,也就是说,Bitmap占用的内存和Java Object占用的内存加起来不能超过Java Object Heap的最大值。这就是为什么我们在调用BitmapFactory相关的接口来处理大图像时,会抛出一个OutOfMemoryError异常的原因。

在3.0以及更高的版本中,Bitmap Memory就直接是在Java Object Heap中分配了,这样就可以直接接受GC的管理。

3.0之前的手机,通过DDMS观看Heap信息的时候不显示native部分分配的内存大小,如图所示,加载了一张7M多的图片,但是显示分配Allocated才2M多。

16c7fd84b24228e3b278c3cd88af1145.png

3.0以后Bitmap分配的内寸在Heap上,如图

d5aa106aa087966be6fb564a5460f2ab.png

3. Native Heap

Native Heap就是在Native Code中使用malloc等分配出来的内存,这部分内存是不受Java Object Heap的大小限制的,也就是它可以自由使用,当然它是会受到系统的限制

二、dalvik虚拟机的垃圾回收

Dalvik虚拟机可以自动回收那些不再使用了的Java Object,也就是那些不再被引用了的Java Object。垃圾自动收集机制将开发者从内存问题中解放出来,极大地提高了开发效率,以及提高了程序的可维护性。

在2.3之前,Dalvik虚拟使用的垃圾收集机制有以下特点:

1. Stop-the-word,也就是垃圾收集线程在执行的时候,其它的线程都停止;

2. Full heap collection,也就是一次收集完全部的垃圾;

3. 一次垃圾收集造成的程序中止时间通常都大于100ms。

在2.3以及更高的版本中,Dalvik虚拟使用的垃圾收集机制得到了改进,如下所示:

1. Cocurrent,也就是大多数情况下,垃圾收集线程与其它线程是并发执行的;

2. Partial collection,也就是一次可能只收集一部分垃圾;

3. 一次垃圾收集造成的程序中止时间通常都小于5ms。

二、Android的内存溢出是如何发生的

Java Object Heap的最小和最大默认值为2M和16M,手机在出厂时,厂商会根据手机的配置情况来对其进行调整,我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误,从而被kill掉。

为什么会出现内存不够用的情况呢?通常是因为内存泄露引起,或者保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么 这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。

三、常见的内存泄露引起OutOfMemory的情况

1.static静态变量

static修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的对象,就容易出现内存泄露的情况。

例1。

1

2

3

4

5

public class ClassName {

private static Context mContext;

}

以上的代码是很危险的,如果将Activity赋值到么mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。

例2。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

private static Drawable background;

@Override

protected void onCreate(Bundle state) {

super.onCreate(state);

TextView label =new TextView(this);

label.setText("Leaks Test");

if (background ==null) {

background= getDrawable(R.drawable.large_bitmap);

}

label.setBackgroundDrawable(background);

setContentView(label);

background是一个静态的变量,它并没有显式的保存Contex的引用,但是当Drawable与View连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。这个引用链如下:

Drawable->TextView->Context

所以,最终该Context也没有得到释放,发生了内存泄露。

使用static静态变量,应注意:

第一,应该尽量避免static成员变量引用资源耗费过多对象。

第二,在static变量引用对象的时候,在被引用对象不再使用的时候,应及时释放引用,做置null操作 。

2.缓存

缓存一种用来快速查找已经执行过的操作结果的数据结构,如果执行一个操作需要比较多的资源并会多次被使用,通常做法会把操作结果进行缓存,以便在下次调用该操作时使用缓存的数据,以提升应用响应速度及性能。在android应用中,比较常用的是缓存图片对象,从网络获取图片,创建一个bitmap对象都是比较耗费资源的操作,缓存图片对象,能够提升应用的体验,但是一不小心就会出现内存泄露。

例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class ImageManager {

private ConcurrentHashMap mBitmapRefCache =new ConcurrentHashMap();

private void recycleAllBitmap() {

for (Iterator> itr = mBitmapRefCache.entrySet().iterator(); itr.hasNext();) {

Map.Entry urlBitmap = itr.next();

String url = urlBitmap.getKey();

Bitmap bitmap = urlBitmap.getValue();

if (bitmap !=null && !bitmap.isRecycled()) {

bitmap.recycle();

}

}

}

recycleAllBitmap方法遍历了mBitmapRefCache中缓存的所有的Bitmap对象,并进行了recycle操作,查看recycle方法的注释,recycle会释放Native Heap中占用的内存,并且告诉虚拟机,如果该图片对象没有被引用的情况下可以被回收。由于mBitmapRefCache中还有引用,gc并不会回收。

在使用缓存的适合应注意:

第一,做好缓存区的缓存对象的淘汰策略,淘汰不再使用的或者使用率较低的对象,减少缓存区的占用

第二,注意要去除被淘汰的对象的引用

第三,做好缓冲区的大小的管理,避免出现缓存对象数据太多,消耗大量内存

第四,使用SoftRefrence代替强引用

3.大对象

android应用中常见的大对象就是Bitmap,可以说出现OutOfMemory问题的绝大多数情况,都是因为Bitmap的问题。

第一、及时的回收。

虽然虚拟机垃圾回收机制会自动回收没有不再使用的Bitmap,但是由于它占用的内存过多,所以很可能会超过java堆的限制,因此,在用完Bitmap时,要及时的recycle掉,recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。

第二、设置一定的采样率。

有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码:

1

2

3

4

5

private ImageView preview;

BitmapFactory.Options options =new BitmapFactory.Options();

options.inSampleSize =2;//图片宽高都为原来的二分之一,即图片为原来的四分之一

Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri),null, options);

preview.setImageBitmap(bitmap);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值