目录

  • 常见的内存泄漏
  • 使用底层包内存泄漏
  • netty中内存泄漏
  • 日常代码中内存泄漏
  • 匿名内部类
  • 非静态内部类
  • Handler内存泄漏
  • 集合中对象没清理
  • File等资源未关闭
  • 监听器未关闭
  • 单例模式


常见的内存泄漏

内存泄漏一般情况不会有,但是有了不太好找。一般内存泄漏产生的原因主要有以下几点。
1.开发人员自己创造出来的内存泄漏代码
2.底层依赖的代码存在问题。
3.系统中依赖的包导致的问题。

使用底层包内存泄漏

netty中内存泄漏


日常代码中内存泄漏

匿名内部类

和前面的非静态内部类一样,匿名内部类也会持有外部类实例的引用。

public class AsyncTaskActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    	...
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAsyncTask();  
                finish();
            }
        });
    }
    void startAsyncTask() { //启动异步执行
        new AsyncTask<Void, Void, Void>() {//异步任务在后台执行耗时任务期间,AsyncTaskActivity被销毁
            @Override
            protected Void doInBackground(Void... params) {
                while (true) ;  //任务持续不销毁
            }
        }.execute();  
    }

在注释1处实例化了一个AsyncTask,当AsyncTask的异步任务在后台执行耗时任务期间,AsyncTaskActivity 被销毁了,被AsyncTask持有的AsyncTaskActivity实例不会被垃圾收集器回收,直到异步任务结束。
解决办法就是自定义一个静态的AsyncTask

public class AsyncTaskActivity extends AppCompatActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startAsyncTask();
                finish();
            }
        });
    }
    void startAsyncTask() {
        new MyAsyncTask().execute();
    }
    //自定义静态的类处理
    private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            while (true) ;
        }
    }
}

非静态内部类

public class SecondActivity {
    private static Object inner;//静态内部类
    private Button button;

    @Override
    protected void on(Bundle savedInstanceState) {
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createInnerClass(); //隐式创建
                finish();
            }
        });
    }
    void createInnerClass() {
        class InnerClass {
        }
        inner = new InnerClass();//内部类的创建
    }
}

非静态内部类会持有外部类实例的引用,如果非静态内部类的实例是静态的,就会间接的长期维持着外部类的引用,阻止被系统回收。

Handler内存泄漏

注意两点可能引起内存泄漏的地方

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

Handler的Message被存储在MessageQueue中,有些Message并不能马上被处理,它们在MessageQueue中存在的时间会很长,这就会导致Handler无法被回收。如果Handler 是非静态的,则Handler也会导致引用它的Activity或者Service不能被回收。

public class HandlerActivity extends AppCompatActivity {
    private Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        final Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mHandler.sendMessageDelayed(Message.obtain(), 60000);
                finish();
            }
        });
    }
}

Handler 是非静态的匿名内部类的实例,它会隐性引用外部类HandlerActivity 。上面的例子就是当我们点击Button时,HandlerActivity 会finish,但是Handler中的消息还没有被处理,因此HandlerActivity 无法被回收。
解决方法就是要使用一个静态的Handler内部类,Handler持有的对象要使用弱引用,并且在Activity的Destroy方法中移除MessageQueue中的消息,如下所示。

public class HandlerActivity extends AppCompatActivity {
    private Button button;
    private MyHandler myHandler = new MyHandler(this);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myHandler.sendMessageDelayed(Message.obtain(), 60000);
                finish();
            }
        });
    }
    public void show() {
    }
    //静态的handler
    private static class MyHandler extends Handler {
        private final WeakReference<HandlerActivity> mActivity;
        public MyHandler(HandlerActivity activity) {
            mActivity = new WeakReference<HandlerActivity2>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            if (mActivity != null && mActivity.get() == null) {
                mActivity.get().show();
            }
        }
    }
    @Override
    public void onDestroy() {
        if (myHandler != null) {
            myHandler.removeCallbacksAndMessages(null);
        }
        super.onDestroy();
    }
}

MyHandler是一个静态的内部类,它持有的 HandlerActivity对象使用了弱引用,并且在onDestroy方法中将Callbacks和Messages全部清除掉。

集合中对象没清理

,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。

Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
    Object o=new Object();
    v.add(o);
    o=null; 
}

所有的Object对象都没有被释放,因为变量v引用这些对象。
还有当集合里面的对象属性被修改后,再调用remove()方法时不起作用。

File等资源未关闭

File等,往往都用了缓冲,不使用的时候应该关闭它们。把他们的引用置为null,而不关闭它们,往往会造成内存泄漏。

监听器未关闭

单例模式

不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收,导致内存泄漏。