您的位置:首页 > 其它

内存泄漏的学习

2016-05-06 15:12 148 查看

内存泄漏介绍

内存泄漏是针对与堆内存而言的。

Java的内存管理就是对象的分配和释放。内存分配是由程序进行的,内存的释放是由GC完成。GC只能回收那些无用,且不被其他对象引用的对象们占用的空间。

从Main方法开始延伸,所有可以到达的对象都是有效对象,组成对象集合,这些不能被回收。其他的孤立对象则是GC回收的目标。

{
Object o = new Object();
}


局部变量的生命周期是在大括号结束。这时候,栈内的o被销毁,则 new Object() 这个对象不在被引用,成为孤立对象,会被GC回收。

所以,内存泄漏的根本原因是:堆内存中的长生命周期的对象持有短生命周期对象的强/软引用,尽管短生命周期对象已经不再需要,但因为长生命周期对象持有对他的引用而导致他不能被回收。

常见的内存泄漏

集合

集合如果只有添加方法,没有删除方法,内存会被大量占用。如果集合是全局的,会造成内存只增不减。


单例

单例对象在被初始化后会在JVM整个生命周期中存在,如果单例持有了外部对象,会造成外部对象不能被回收,致使内存泄漏。

public class Test{
private static Test test;
private Context context;

private Test(Context context){
this.context = context;
}

public static Test getInstance(Context context){
if(test == null){
test = new Test(context);
}

return test;
}
}


如果传入的context是一个activity,会使activity一直存在于内存中,不能被释放。

Android中的各种组件

BroadCastReceiver,ContentObserver,Cursor等,在Regist后,记得在结束时调用unRegister或close(),并且,不要将Activity作为成员变量使用,例如上面单例的使用。如果一定需要,则记得使用 WeakReference。

非静态内部类

public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mResource == null){
mResource = new TestResource();
}
//...
}

class TestResource {
//...
}
}


外部类的静态成员mResource的生命周期是整个程序的运行时间,而内部类的对象默认持有对外部类对象的一个引用,所以会导致Activity无法得到释放。

可以将内部类定义为静态内部类,则可以将内部类和外部类的对象的关系切断,只与类有关。

线程造成的内存泄漏

因为线程的生命周期不可控,如果线程保存了对外部对象的引用,会造成外部对象的不可释放。

Handler造成的内存泄漏

如果handler发送的message没有被处理,那么MessageQueue将一直保留对handler,message的引用,而且handler与Activity的生命周期并不一致,可能会导致activity不能顺利释放。

public class SampleActivity extends Activity {

private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);

// Go back to the previous Activity.
finish();
}
}


以上代码,发送了延时的消息,这消息并未被处理时,该页面退出会导致该页面无法被回收。

注意事项

对activity的引用要考虑activity的生命周期,不过不能控制在其生命周期内,可以考虑用getApplicationContext(),避免activity长时间被占用。

尽量不要在静态内部类使用非静态的成员变量,必须要用,也要在适当的时候将其置为空;也可以在静态内部类中使用弱引用来引用外部成员变量。

Handler持有的引用对象最好为弱引用,资源释放的时候可以清空handler中的消息。

线程处理耗时操作,在页面返回时即使取消。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: