Anfroid性能优化之内存泄露篇

首先看看内存泄露与内存溢出的概念:

1、内存泄漏:

当出现对Activity、View或drawable等类的对象长期持有无用的引用,就会造成被引用的对象无法在GC时回收,而是长期占用堆空间,此时就会发生内存泄漏。
简单来说,就是保留下来却永远不再使用的对象引用。

2、内存溢出:

如果应用程序在消耗光了所有的可用堆空间(16M到48M),那么再试图在堆上分配新对象时就会引起OOM(Out Of Memory Error)异常,此时应用程序就会崩溃退出。

3、两者的区别:

简单的说,就是内存溢出是占用内存太大,超过了其可以承受的范围;
内存泄漏是回收不及时甚至是没有被回收,而在推空间中产生的许多无用的引用。
于是过多的内存泄漏就会导致内存溢出,从而迫使程序崩溃退出。

下面重点来谈谈android中有关内存泄露的问题。

虽然Java有专门的GC来回收不再使用的对象,但是我们不能完全依靠GC来彻底回收对象,在编码的过程中如不遵守一定的规则就会造成内存泄露。主要有以下三种:

1,静态变量持有对象,特别是生命周期比整个应用短的对象 

2,非静态内部类隐式持有外部类对象,特别是内部类生命周期比外部类长 

3,资源打开未关闭,如文件,数据库等

一,   静态变量持有对象,特别是生命周期比整个应用短的对象

泄漏 activity 最简单的方法就是在 activity 类中定义一个 static 变量,并且将其指向一个运行中的 activity 实例。如果在 activity 的生命周期结束之前,没有清除这个引用,那它就会泄漏了。这是因为activity(例如 MainActivity) 的类对象是静态的,一旦加载,就会在 APP 运行时一直常驻内存,因此如果类对象不卸载,其静态成员就不会被垃圾回收。

二非静态内部类隐式持有外部类对象,特别是内部类生命周期比外部类长。

1 handler内存泄露

最常见的就是在actvity中使用handler。创建非静态内部的时候,该内部类默认会持有外部类的引用,如果外部类的生命周期比较短,那么内部类就不会释放外部类对象的引用,这就造成了内存泄露。

public class HandlerActivity extends Activity{ 
   private final Handler mHandler = new Handler() { 
       @Override 
       public void handleMessage(Message msg) { 
           // ... 
       } 
   }; 
 
   @Override 
   public void onCreate(Bundle savedInstanceState) { 
       super.onCreate(savedInstanceState); 
       setContentView(R.layout.activity_main); 
       mHandler.sendMessageDelayed(Message.obtain(), 1000); 
         finish(); 
} 


原因分析:

1.               Handler 的生命周期与Activity 不一致。当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。

2.               handler 引用 Activity 阻止了GCAcivity的回收。

Activity finish后,meaasge会继续存在UI线程的messagequeue中,。而该消息引用了ActivityHandler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的Activity泄露。

publicboolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = newRuntimeException(
                    this + "sendMessageAtTime() called with no mQueue");
            Log.w("Looper",e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg,uptimeMillis);
}


修改方法:

·        使用显形的引用,1.静态内部类。 2. 外部类

·        使用弱引用 2.WeakReference

  • static class ExerciseHandler extends Handler{
              private SoftReference<ExerciseActivity> exerciseActivitySoftReference = null;
     
              public ExerciseHandler(ExerciseActivity exerciseActivity){
                  exerciseActivitySoftReference = new SoftReference<ExerciseActivity>(exerciseActivity);
              }
     
              @Override
              public void handleMessage(Message msg) {
                  ExerciseActivity exerciseActivity = exerciseActivitySoftReference.get();
                  if(null != exerciseActivity){
                      super.handleMessage(msg);
                      switch (msg.what) {
                          case MSG_XX:
                              exerciseActivity.***;
                              break;
                          default:
                              break;
                      }
                  }
              } public void onDestroy() { 
         mHandler.removeMessages(MESSAGE_1); 
         mHandler.removeMessages(MESSAGE_2); 
         mHandler.removeMessages(MESSAGE_3); 
     
         // ... ... 
     
         mHandler.removeCallbacks(mRunnable); 
     
         // ... ... 
     }   


 三.资源打开未关闭,如文件,数据库等

  • IO操作后,没有关闭文件导致的内存泄露,比如CursorFileInputStreamFileOutputStream使用完后没有关闭,这种问题在Android Studio 2.0中能够通过静态代码分析检查出来,直接改善就可以了;
  • 自定义View中使用TypedArray后,没有recycle,这种问题也可以在Android Studio 2.0中能够通过静态代码分析检查出来,直接改善就可以了

Context 内存泄露

某些地方使用了四大组件的context,在离开这些组件后仍然持有其context导致的内存泄露,这种问题属于共识,在编写代码的过程中就应该按照规则来,使用ApplicationContext就可以解决这类内存泄露的问题了

常见的解决办法:

通过工具检查程序运行后的内存泄露

通过上面的步骤,应用中的大部分内存泄露问题都能够得到解决,还有一些内存泄露,需要运行程序,分析运行后的内存快照来解决,比如注册之后没有反注册、类中的静态成员变量导致的内存泄露、SDK中的内存泄露等。解决这类问题可以分两步进行:

 

通过内存泄露检测工具先定位是哪有问题,内存泄露的检测有两种比较便捷的方式:1、一种是使用开源项目Leakcanary,需要添加到代码中,运行后生成分析结果;2、另一种方式是使用adb shell dumpsys meminfo packagename -d命令,在进入一个界面之前查看一遍Activity和View的数量,在退出这个界面之后再查看一遍Activity和View的数量,对比进入前和进入后Activity和View数量的变化情况,如果有差异,则说明存在内存泄露(在使用命令查看Activity和View的数量之前,记得手动触发GC)。

 


 

 

 

 

 

 

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值