您的位置:首页 > 移动开发 > Android开发

Android内存泄露分析以及处理

2017-02-28 10:34 211 查看
基础知识:
java的内存分配

静态存储区:编译时就分配好,在程序整个运行期间都存在。它主要存放静态数据和常量;

栈区:当方法执行时,会在栈区内存中创建方法体内部的局部变量,方法结束后自动释放内存;

堆区:通常用来存放new出来的对象。由java垃圾回收期回收。

四种引用类型的介绍:

强引用(StrongReference):JVM 宁可抛出 OOM ,也不会让 GC 回收具有强引用的对象;

软引用(SoftReference):只有在内存空间不足时,才会被回的对象;

弱引用(WeakReference):在 GC 时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,

都会回收它的内存;

虚引用(PhantomReference):任何时候都可以被GC回收,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,

就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否存在该对象的虚引用,来了解这个对象是否将要被回收。可以用来作为GC回收Object的标志。

内存泄露检测工具
1. MAT(Memory Analyzer Tool),下载地址:
http://www.eclipse.org/mat/downloads.php
2. 强大的开源内存检测工具 LeakCanary。

leakcanary 是一个开源项目,一个内存泄露自动检测工具,是著名的 GitHub开源组织Square贡献的,它的主要优势就在于自动化过早的发觉内存泄露、配置简单、抓取贴心,缺点在于还存在一些bug,不过正常使用百分之九十情况是OK的,其核心原理与MAT工具类似。

因为配置十分简单,只需要几句话就好!!!这里就不多说了,大家可以看官方文档:
https://github.com/square/leakcanary 1.在使用单例模式时,如果我们传入的Context是Activity的Conetxt,当这个对应的Activity退出时,

由于该Context的引用被单例对象所持有,其生命周期等于整个应用的生命周期,所以当前Activity退

出时它的内存并不会回收,所以会造成内存泄露
code:
public class SingleDemo{
private static SingleDemo mSingleDemo;
private Context mConetxt;

private SingleDemo(Context context){
//获取Application的Context避免有泄露
this.mContext=context.getApplicationContext();
}

private static SingleDemo getInstance(Context context){
if(mSingleDemo == null){
mSingleDemo=new SingleDemo();
}
return mSingleDemo;
}

}


2.Handler使用不当造成的内存泄露

Handler造成的内存泄露在Android开发中是最常见的,我们知道,Handler,Message以及MessageQuene是相

互关联在一起的,万一Handler发送的Message未被处理,则该Message以及发送它的Handler对象都会一直

被MessageQuene持有。
可以通过弱引用和static内部类来防止内存泄露
public class HandlerActivity extends AppCompatActivity{
//声明为静态的内部类,因为非静态内部类会持有外部类的引用,有可能会造成内存泄露
//声明为静态内部类后,其存活期就与activity的生命周期无关了
private static final class MyHandler extends Handler{
//不过倘若用到 Context 等外部类的 非static 对象,还是应该通过弱引用传入
private final WeakReference<HandlerActivity> mActivity;

@Override
public void onHandlerMessage(Message msg){
super(msg);
HandlerActivity handlerActivity=mActivity.getActivity();
if(handlerActivity!=null){
//do something...
}

}

}

private MyHandler mMyhandler=new MyHandler(this);
//匿名的静态内部类绝不会持有外部类的引用
private static final Runnable RUNNABLE=new Runnable(){
@Override
public void run(){}
}

protect void onCreate(Bundle saveInstanceStates){
super.onCreate(saveInstanceStates);
setContentView(R.layout.activity_main);
mMyhandler.postDelayed(RUNNABLE,500);
}

}


综述:推荐使用静态内部类+弱引用 WeakReference 这种方式,但要注意每次使用前判空。

这里再提下java的几种引用类型:StrongReference,SoftReference,WeakReference 和 PhatomReference:

回收时机 级别用途生存时间

StrongReference  绝不强引用  对象的一般状态JVM停止运行时终止
SoftReference
内存不足时 软引用
联合ReferenceQuene构造的有效期短/占内存大/声明周期长的对象的二级高速缓冲器(内存不足时清空)内存不足时终止
WeakReference
在垃圾回收时    弱引用 联合ReferenceQuene构造的有效期短/占内存大/声明周期长的对象的一级高速缓冲器(系统发生gc则清空)gc运行后终止
PhatomReference 在垃圾回收时虚引用联合ReferenceQuene来跟踪对象被gc回收的活动gc运行后终止

继续回到主题。前面所说的,创建一个 静态Handler内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler 持有的对象,
但是这样做虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理的消息,所以我们在 Activity 的 Destroy 时或者 Stop 
时应该移除消息队列 MessageQueue 中的消息。
@Override
protected void onDestory(){
super();
mMyhandler.removeCallBacksAndMessages(Object token);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: