【原创】android内存管理-内存泄漏原因

时间:2020-12-15 15:23:23

转载请注明出处 http://www.cnblogs.com/weiwangnuanyang/p/5704596.html

先讲一下内存泄漏的概念:内存泄露是指无用对象持续占有内存,或者内存得不到及时释放,从而造成的内存空间的浪费。

我们都知道,Java中对象的存储在堆中,它们由GC回收。GC为了能够正确释放对象,会监控每个对象的运行状况(申请、引用、被引用、赋值等)。Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。

因此Java中造成内存泄漏的一般原因都是长生命周期的对象持有了短生命周期的对象,使得短生命周期的对象不能被释放;或者资源未及时关闭。

下面总结了一些造成android中内存泄漏的常见原因:

1. 大量同一个类的对象被常驻内存的Map(比如单例模式的静态成员变量)持有,未及时移除。

比如观察者模式中的监听者,注册后没有反注册。

再比如使用一个很重的对象作为Map的key,导致这个类所引用的所有对象都不能释放。

所以如果注册监听时,监听的对象常驻内存,一定要反注册。尽量不使用很重的对象作为Map的key,如果有这样的需求,一定要及时清空Map.

2. Activity中使用非静态内部类或者匿名内部类,或者引用着Activity的类被其他长生命周期类持有。Activity持有其View层以及所有相关联资源的引用,如果无用的Activity得不到释放,会泄漏大量资源。

下面列举一些常见的情况。

(1)将Activity作为单例模式的静态成员变量。

(2)静态对象间接引用Activity.如下面代码的引用关系mDrawable -> ImageView -> MainActivity,导致Activity泄漏。

public class MainActivity extends Activity {
private static Drawable mDrawable;
@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = new ImageView(this);
mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
iv.setImageDrawable(mDrawable);
}
}

(3)Activity的内部类作为其静态成员变量。非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,如下Demo会持有MainActivity,

public class MainActivity extends Activity
{
static Demo sInstance = null;
public void onCreate(BundlesavedInstanceState){
} class Demo{
void do Something(){
}
}
}

(4)Handler作为内部类引起的内存泄漏。如果Handler是非静态内部类,Handler 会持有Activity的引用。当Activity退出而Handler还有延迟处理的消息没有处理完,会导致Activity不能回收,反复如此会导致内存泄露。

具体分析可以参考 http://droidyue.com/blog/2014/12/28/in-android-handler-classes-should-be-static-or-leaks-might-occur/

(5)线程引起的内存泄漏。下面的代码中Tread运行不定时,在run方法未执行完的期间会一直持有Activity的引用。而AsyncTask的问题更严重写,AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。

 SampleActivity extends Activity {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
doSomething();
return null;
}
}.execute(); new Thread(new Runnable() {
@Override
public void run() {
doSomething();
}
}).start();
}

我们首先想到的解决方法是使用静态内部类,但是静态内部类又不持有Activity对象,所有当需要使用Activity的时候可以使用弱引用。

private static class MyHandler extends Handler {

    private final WeakReference<Context> context;

    private MyHandler(Context context) {
this.context = new WeakReference<Context>(context);
} @Override
public void handleMessage(Message msg) {
switch (msg.what) { }
}
} private final MyHandler mHandler = new MyHandler(this); private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { }
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
// 发送一个10分钟后执行的一个消息
mHandler.postDelayed(sRunnable, 600000);
}

(6)Context引起的泄漏。一个app的Application会持有一个生命周期与app一样长的Context,每一个组件会持有一个与组件生命周期一样长的Context.当比Activity生命周期长的对象持有了Activity的Context,会引起Activity不能被回收。所以在允许的情况下,尽量使用ApplicationContext。

3. 资源未及时关闭

在实际开发中,经常需要在程序中打开一些物理资源,如数据库连接、网络连接、磁盘文件等,打开这些物理资源之后必须显式关闭,否则将会引起资源泄漏。

JVM的垃圾回收机制不会回收这些资源,垃圾回收机制属于java内存管理的一部分,它只是负责回收堆内存中分配出来的内存,至于程序中打开的物理资源,垃圾回收机制是无能为力的。

通常在finally块中关闭资源,有一个细节就是,最好在关闭资源时进行判空。

4. 合理管理Bitmap

以上部分内容摘自:

http://blog.csdn.net/mxm691292118/article/details/51020023