安卓内存相关

一、内存泄露的定义:
    内存泄露是指不再使用的内存仍然占用着内存空间,因为程序中仍然保存着对它的引用,而使得GC无法将它回收或得到及时释放,从而造成的内存空间浪费的问题,称为内存泄露。

二、内存泄露的根本原因:
   长生命周期的对象持有短生命周期对应的引用,因为短生命周期对象可能不再使用,而因为长生命周期对象持有着对其的引用,因此GC无法将其进行回收。

三、内存泄露(Memory Leak)与内存溢出(OOM)之间的区别:
    内存溢出(OOM)是指程序在申请内存时,没有足够的内存空间供其使用,出现OOM;
    Android Dalvik虚拟机的内存大致可以分为三类:Java Heap Object,Bitmap Memory, Native Object;Dalvik虚拟机在启动的时候,就是通过读取系统属性dalvik.vm.heapsize的值来获得Java Object Heap的最大值(一般 为16M),当应用使用的内存超过16M时,便会发生OOM;而Bitmap Memory是专门用来处理图像的,Bitmap直接在Java native Object中进行分配的,故平时在进行图片开发时,才会对图片的处理如此谨慎,避免发生OOM;
    内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,即发生OOM;


四、造成内存泄露的情况包括有:
1)静态集合类造成的内存泄露;静态变量存储在方法区中,其生命周期与程序相同,而其内部又保存着对对象的引用,当这些对象其实不会再使用时,也依然无法释放;  
     因此当该静态集合类不再使用,或者静态集合类中保存的对象确定不会再使用时,要注意设为null;
          static Vector v = new Vector(10);
              for (int i = 1; i < 100; i++) { 
                   Object o = new Object();
                    v.add(o);
                    o = null; // 设置null也无法将其内存回收,因为Vector中仍保存着对该内存的引用
               }

2)在观察者模式中,添加对一个对象的监听器,当对象不再使用时,而未对监听器进行释放。
这个在Android中情况尤为严重,比如我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。
对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。
还有如调用了registerReceiver方法,而没有调用unregisterReceiver方法,造成的内存泄露等;

3)单例模式引起的内存泄露:

class A {}
 
  class Test {
      private static Test instance = new Test(new A());
      private A a;
      private Test(A a) {
          this.a = a;
      }
 
      public Test getInstance() {
          return instance;
      }
  }

设计上出现的问题,因为静态变量存储在方法区,因此instance的生命周期与应用相同,而instance中又保存了对A对象的引用,使得即使A对象不再使用,也无法对其进行释放。

4)变量生命周期使用不当:

class Server{
    private String msg;
    public void recvMsg(){
        readMsg(msg);
        save(msg);
        // 这里msg被保存之后已经无用,但是其生命周期将与Server保持一致,不能被回收,如果msg太大,将会造成不可预测的意外,安全的做法是加一行代码:msg=null;
    }
}

1、变量的作用域需要合理设置,类的成员变量生命周期与类对象相同,对于一些只需要短生命周期的变量,则会造成内存空间的浪费;
而这个也需要与内存抖动之间进行平衡比较,避免短时间内创建大量的临时性变量而造成内存抖动。

2、同理对于static静态变量,也应当遵循谨慎的原则进行使用;

3、在Android中最突出的问题便是对Context的引用造成的内存泄露。因为Activity对象本身引用的资源较多,其无法进行释放造成的资源浪费效果将会很明显;因此应当注意避免让长生命周期的对象引用Activity,可以尝试引用Application Context;

5)内部类与外部类之间的引用造成的内存泄露;由于内部类保存着对外部类的引用,若未对其进行释放,外部类的内存空间也无法释放;

1、最典型的便是Handler造成的内存泄露:

由于MyHandler是内部类,初始化MyHandler,其内部会隐式地保存对其外部类即Activity的引用;
    使用MyHandler postDelayed一个Runnable,该操作会send一个Message到MessageQueue中,而Message对象msg中的target变量将会指向myHandler,即msg保存有对myHandler的引用
    又MessageQueue是针对线程存在,当msg入队列还未得到处理时,Activity被关闭,此时msg依然存在与MessageQueue中;

    又由前面得知msg保存着对myHandler的引用,而myHandler保存着对Activity的应用;因此Activity关闭后,其内存仍然无法得到回收,即造成内存泄露。

2、解决Handler内存泄露的方法:使用弱引用及静态内部类的方法;

6)涉及到ListView优化中的未使用convertView,过多使用自己创建的View;

1、错误用法:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = inflater.inflate(R.layout.activity_main, parent, false);
    // ......
    return view;
}

2、由ListView的源码及工作原理,来看一下ListView的源码:

    ListView填充及获取View的方法如下:

由RecycleBin机制,ListView会首先尝试获取ActiveView[]中存储的View;然后再通过obtainView来获取View;

obtainView方法中首先通过getScrapView获取ScrapView[]中存储的View,ScrapView中存储的View便是划出界面废弃的View存储起来,便于再次重复利用;
可以看到在child = mAdapter.getView(position, scrapView, this);方法中来获得View,而scrapView即对应重写BaseAdapter方法中的convertView变量;不使用convertView则会造成废弃的View无法重复利用,同时大量创建的View,当View移出界面之后,而无法得到及时回收,造成内存泄露。

3、正确的用法:

7)资源没有关闭造成的内存泄露:

1、Cursor、File等资源型对象:

资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。
它们的 缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。
如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。

2、WebView对象:要及时调用destory()方法销毁该对象;
3、Bitmap的回收:Bitmap占用的内存空间较大,在不对其使用时,要注意调用recycle()方法对其进行回收。具体参考图片优化
4、InputSteam,OutputStream等输入输出缓冲流在使用完成后要注意进行关闭。
5、动画未关闭:属性动画中有一类无限循环的动画,若Activity销毁时并未关闭动画,则动画会一直运行,由于属性动画持有View对象,View对象持有其相关的Activity对象,会造成Activity无法释放而内存泄露

五、处理内存泄露的方法:
除了针对上述内存泄露的原因进行相应的处理外,还可以通过mat工具对内存泄露进行分析。
http://www.jianshu.com/p/c49f778e7acf
http://blog.csdn.net/yulianlin/article/details/50393872 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值