目录
- 常见的内存泄漏
- 使用底层包内存泄漏
- 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正常回收,导致内存泄漏。