背景:

程序的运行离不开内存,尽管硬件技术的发展也让手机的内存不断增大(比如我的OPPO R7s是256M),但是不恰当的编程习惯仍然会导致内存泄漏的发生,这将是一场灾难,所以我们让然应该去避免。

内存泄漏与内存溢出的关系:

内存泄漏为内存溢出埋下隐忧,因为内存泄漏的实质就是需要释放的内存被生命周期长的对象继续持有导致得不到释放,系统无法重新分配这些内存,随着软件的使用内存资源越来越少,导致内存不够,出现溢出(OOM),进而crash掉.

获取系统为每个虚拟机实例(每个Application)分配的内存是多大,可以通过下面这个代码片段获取一下:

/**
* the maximum amount of memory that the virtual machine will attempt to use, measured in bytes.
*/
 Runtime runtime = Runtime.getRuntime();
 long l = runtime.maxMemory()/1024/1024;//以byte为单位返回的

收集一些常见的小例子:

1:非静态内部类

public class MainActivity extends AppCompatActivity {

    private static InnerClass innerClassInstance;//静态对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_create_instance).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                innerClassInstance = new InnerClass();//创建静态对象
                finish();//销毁这个Activity即被隐式持有的外部类
            }
        });

    }

    class InnerClass {
        //这就是内部类,隐式持有外部类
    }
}

分析:因为全局变量innerClassInstance声明成了静态的,所以其生命周期和此应用的声明周期一样长,并且InnerClass持有Activity这个外部类,这就导致了Activity被销毁后并不能被系统回收,这块内存就无法重新分配继续使用,这就造成了内存泄漏。

2:匿名内部类

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_create_instance).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startNewThread();//开启一个新线程
                finish();//销毁这个Activity即被隐式持有的外部类
            }
        });
    }

    public void startNewThread() {
        new Thread(new Runnable() {//匿名内部类隐式持有外部类的引用
            @Override
            public void run() {
                //执行耗时操作的子线程,在Activity销毁了还没结束
            }
        }).start();
    }
}

分析:因为这个开启的子线程在执行一个耗时操作,在Activity已经销毁时还没执行完成,隐式持有的外部类Activity,导致不能被回收。

解决办法:匿名内部类改成静态内部类

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_create_instance).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new MyThread().start();//开启一个新线程
                finish();//销毁这个Activity即被隐式持有的外部类
            }
        });
    }

    private static class MyThread extends Thread {//静态的内部类不再持有外部类Activity
        @Override
        public void run() {
            //执行耗时操作的子线程,在Activity销毁了还没结束
        }
    }
}

3.Handler内存泄漏,注意两点可能引起内存泄漏的地方

  1. 非静态内部类(第一种情况)
  2. 有延迟消息未在onDestory中移除

4.Context和单例的内存泄漏

解决方案:能使用Application上下文的地方尽量使用Application的上下文;

5.Cursor,File等资源未关闭

分析:资源对象比如Cursor、File等,往往都用了缓冲,不使用的时候应该关闭它们。把他们的引用置为null,而不关闭它们,往往会造成内存泄漏。

解决方案:在资源对象不使用时,一定要确保它已经关闭,通常在finally语句中关闭,防止出现异常时,资源未被释放的问题。

6.Register的广播,EventBus等等都要unRegister

7.Bitmap释放

临时创建的某个相对比较大的bitmap对象,在经过变换得到新的bitmap对象之后,应该尽快回收原始的bitmap,这样能够更快释放原始bitmap所占用的空间。
避免静态变量持有比较大的bitmap对象或者其他大的数据对象,如果已经持有,要尽快置空该静态变量。