(原创)内存泄漏的几种情况解决方式

Android开发,内存优化是一个很重要的问题,今天就对这方面的问题,做一个较为详细的介绍

在讲内存优化之前,先要将内存环境问题说清楚

java中内存环境分为三种

栈:用于存放基本数据类型和对象的引用

堆:用于存放创建出来的对象或者数组,由GC管理,被所有线程共享

方法区:也叫静态区,包含了静态变量和class对象,被所有线程共享

内存泄漏的原因,具体可以分为下面两种情况

1,当一个对象已经不需要使用时,准备被回收时,被另外一个对象持有了引用导致无法回收

2,有些对象只有有限的生命周期,在生命周期本该结束时,仍被引用。

内存泄漏的累积,会导致内存溢出。即OOM

接下来讲几种常见的内存泄漏情况和处理方法:

1,单例导致的内存泄漏

先来看以下代码,是一个简单的单例模式

public class SingerDemo {

    private Context context;
    private static SingerDemo instance;

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

    public static SingerDemo getInstance(Context context) {
        if (instance == null) {
            instance = new SingerDemo(context);
        }
        return instance;
    }
}

在单例的构造方法里面,会传入一个Context对象

如果我们传入的是Activity的Context,在Activity关闭时,这个单例的对象仍然持有Activity的引用

从而导致内存泄漏

这个解决的方法,就是在单例的构造方法里面做处理

使用getApplicationContext()获取到应用的Context

具体代码如下

    private SingerDemo(Context context) {
//        this.context = context;
        this.context = context.getApplicationContext();
    }

这个例子也告诉我们,在使用Context的时候,要注意区分情况。

2,非静态内部类创建静态实例导致的内存泄漏

再来看下面的代码

public class MainActivity extends AppCompatActivity {
    private static MainClass mainClass = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (mainClass == null) {
            mainClass = new MainClass();
        }
    }

    private class MainClass {

    }
}

这种情况下,非静态的内部类MainClass默认持有了MainActivity的引用

从而导致他的静态实例对象一直持有Activity的引用

在Activity关闭时无法释放

从而导致MainClass无法被内存回收,导致内存泄漏

解决方法

把内部类改成静态内部类即可

3,handler造成的内存泄漏

Handler的生命周期和Activity的生命周期是不一致的,

所以Activity关闭的时候

Handler可能还存在内存

看下面这个例子

public class MainActivity extends AppCompatActivity {

    private final Handler myhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myhandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                
            }
        }, 1000 * 60 * 10);
    }

}

当Handler延迟执行消息的时候,把Activity关闭

这个时候Handler持有了Activity的对象

导致了内存泄漏

解决的方法有两种,第一种

就是在Activity关闭时,remove掉Handler的消息

removeMessages:移除单个message

removeCallbacksAndMessages:移除全部message和回调

另外一种办法

1,将handler声明为静态的

2,通过弱引用的方式引入Activity,

对相关方法和View进行操作

代码如下:

    private static class MyHandler extends Handler {
        private WeakReference<MainActivity> mWeakReference;

        public MyHandler(MainActivity activity) {
            mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity mainActivity = mWeakReference.get();
            switch (msg.what) {
                case 0x001:
                    if (mainActivity != null) {
                        mainActivity.mTextView.setText(msg.obj + "");
                    }
                    break;
                default:
                    break;
            }
        }
    }

4,线程导致的内存泄漏

线程导致的内存泄漏,处理方式和Handler一样

将Thread和Runnable

都声明为静态的

用弱引用的方式获得Activity对象

或者在合适的生命周期,

去结束这个线程

5,Webview造成的内存泄漏

Webview造成内存泄漏的原因有很多种

有时候需要移动端和网页端对应处理

这里提供一种解决方法

1,将Webview所处的Activity放在一个单独的进程当中

在Activity的配置清单里添加android:process=""属性即可

process属性的值即是你的进程的名字

2,关闭Activity时,调用下面这个方法,去杀掉这个进程即可

android.os.Process.killProcess(android.os.Process.myPid());

更多解决方式,可以参考这篇文章

WebView内存泄漏--解决方法小结

最后分享几个关于内存优化的点

1,频繁的字符串拼接使用StringBuilder代替String

2,ArrayMap,Sparse代替HashMap

3,不要频繁地创建和回收大量对象,避免内存抖动

因为在调用GC的过程中,会停住其他进程

一旦要处理的对象过多,就会造成UI卡顿等问题

4,复用系统自带的资源,ListView的item复用等

5,避免在onDraw方法里创建对象

6,及时关闭Cursor对象

7,注册和取消注册要成对出现。如广播的注册和取消注册

猜你喜欢

转载自blog.csdn.net/Android_xiong_st/article/details/107403839