内存泄漏
什么是内存泄露?
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,
造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
Android发生内存泄漏的常见情况
静态变量
静态变量的生命周期和应用的生命周期一样长。如果静态变量持有某个Activity的context,则会引发对应Activity无法释放,导致内存泄漏。如果持有application的context,就没有问题(以下例子是指Activity销毁时没有释放的情况)
常见的有:
单例模式:内部实现是静态变量和方法
静态的View:view默认持有Activity的context
静态Activity
packagecom.example.testmemoryleak;
importandroid.content.Context;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.support.v7.app.AppCompatActivity;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.Button;
importcom.squareup.leakcanary.LeakCanary;
publicclassMainActivityextendsAppCompatActivity{
privateButton btn;
privateButton btn1;
privatestaticContext StaticVarible;
privateHandler mHandler;
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button);
btn.setOnClickListener(newView.OnClickListener() {
@Override
publicvoidonClick(View v){
MainActivity.this.finish();
}
});
StaticVarible =this;
LeakCanary.install(getApplication());
}
privatestaticclassNoLeakHandlerextendsHandler{
@Override
publicvoidhandleMessage(Message msg){
super.handleMessage(msg);
}
}
}
解决方法:
再ActivityDestory时把静态变量置空即可
@Override
protectedvoidonDestroy(){
StaticVarible =null;
super.onDestroy();
}
匿名内部类或者非静态内部类
常见的包括
Handler,AsyncTask,TimerTask等,一般在处理多线程任务的时候
非静态的内部类和匿名内部类都会隐式地持有其外部类的引用(否则怎么访问外部类的非静态成员呢?),静态的内部类不会持有外部类的引用
Java中的类可以是static吗?答案是可以。在java中我们可以有静态实例变量、静态方法、静态块。类也可以是静态的。
java允许我们在一个类里面定义静态类。比如内部类(nested class)。把nested class封闭起来的类叫外部类。在java中,我们不能用static修饰顶级类(top level class)。只有内部类可以为static。
静态内部类和非静态内部类之间到底有什么不同呢?下面是两者间主要的不同。
(1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。
(2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
(3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。
那么修改方法 1.静态内部类 2.销毁前及时处理非静态内部类
在这里插入代码片
packagecom.example.testmemoryleak;
importandroid.content.Context;
importandroid.os.Handler;
importandroid.os.Message;
importandroid.support.v7.app.AppCompatActivity;
importandroid.os.Bundle;
importandroid.util.Log;
importandroid.view.View;
importandroid.widget.Button;
importcom.squareup.leakcanary.LeakCanary;
publicclassMainActivityextendsAppCompatActivity{
privateButton btn;
privateButton btn1;
privatestaticContext StaticVarible;
privateHandler mHandler;
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button);
btn.setOnClickListener(newView.OnClickListener() {
@Override
publicvoidonClick(View v){
MainActivity.this.finish();
}
});
btn1 = (Button)findViewById(R.id.button1);
btn1.setOnClickListener(newView.OnClickListener() {
@Override
publicvoidonClick(View v){
mHandler =newHandler(){
@Override
publicvoidhandleMessage(Message msg){
super.handleMessage(msg);
}
};
Message message = Message.obtain();
message.what =1;
mHandler.sendMessageDelayed(message,5*60*1000);
/*
mHandler = new NoLeakHandler();
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessageDelayed(message,5*60*1000);
Log.i("weijuncheng", String.valueOf(mHandler.hasMessages(1)));
*/
}
});
StaticVarible =this;
LeakCanary.install(getApplication());
}
@Override
protectedvoidonDestroy(){
//mHandler.removeCallbacksAndMessages(null);
StaticVarible =null;
super.onDestroy();
}
privatestaticclassNoLeakHandlerextendsHandler{
@Override
publicvoidhandleMessage(Message msg){
super.handleMessage(msg);
}
}
}
如上,Activity销毁时非静态内部类mHandler中还有未处理的消息,造成无法释放其引用的Activity对象
修改方案:
使用静态内部类(必要时结合WeakReference,弱引用方式
集合中的对象未去清理造成的内存泄漏
如果一个对象放入到ArrayList、HashMap等集合中,这个集合就会持有该对象的引用。
当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),
这个对象就造成了内存泄露。并且 如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄露了
。所以在使用集合时要及时将不用的对象从集合remove,或者clear集合,以避免内存泄漏。
资源对象
资源对象未关闭:BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,使用后未关闭会导致内存泄漏。因为资源性对象往往都用了一些缓冲,缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果仅仅是把它的引用置null,而不关闭它们,也会造成内存泄漏