您的位置:首页 > 移动开发 > Android开发

Android 常见的内存泄露

2017-08-09 17:46 441 查看
当一个对象不再需要时,本应该被回收,而被另一个正在使用的对象(可能是静态对象,也有可能是匿名内部类对象,等等)引用导致不能正常被回收,就会一直存活在堆中,造成内存泄露。内存泄露会使程序造成程序OOM(Out Of Memory),导致应用Crash。

单例造成的内存泄漏

由于单例的静态特性,使得单例对象和应用的生命周期一样长。所以被单例对象引用的对象,如果不主动置为null释放对象,那么被引用的对象便和单例对象“共生死”,即使被引用的对象不需要了,也不能被回收,从而造成内存泄露。

如例子:

public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}


这样写本来没错,如果你这么用的话:

public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
AppManger appManager = AppManager.getInstance(this);
}
}


那MainActivity就别想被回收了,给我老实待在堆内存中吧!!原因是被单例AppManager所引用。改善的方法:

public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
AppManger appManager = AppManager.getInstance(getApplicationContext());
}
}


这样使用的话,MainActivity就不会被引用,如果没有其他地方引用MainActivity的话,等finish了,就可以等着被回收了。

非静态内部类造成的内存泄露

非静态内部类(包括匿名内部类)会持有外部类对象的强引用(如 Activity),所以很容易导致内存泄露,比如 Handler, Runnable 。

Handler内存泄露

public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Something
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
finish();
}
private void loadData() {
Message message = Message.obtain();
mHandler.sendMessageDelayed(message, 100000);
}
}


因为mHandler是MainActivity的内部类,所以隐式持有MainActivity这个实例。由于Handler还有没有处理的Message,而Message持有mHandler实例,此时即使MainActivity关掉了,内存资源也无法及时被回收,造成内存泄露。解决办法:

public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
finish();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessageDelayed(message, 100000);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}


把非静态内部类声明为静态内部类(其实就相当于单独的一个类,写在某个类里面而已),这样的话mH
4000
andler就不再直接持有MainActivity实例了。

因为要用到MainActivity实例,使用弱引用方式引用MainActivity实例,这样即使mHandler还有没处理的Message,等MainActivity关掉了,MainActivity资源也可以正常被回收。

虽然步骤1和2避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该调用mHandler的removeCallbacksAndMessages或者removeCallbacks移除消息队列中的消息。

Runnable内存泄露

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
finish();
}
}


和Handler一样,因为new出来的Thread匿名内部类也隐式持有MainActivity实例,所以会造成内存泄露,解决的办法如解决Handler内存泄露的原理一样,不再赘述。

资源未关闭造成的内存泄露

BroadcastReceiver,ContentObserver 之类的没有解除注册

Cursor,Stream 之类的没有 close

无限循环的动画在 Activity 退出前没有停止

一些其他的该 release 的没有 release,该 recycle 的没有 recycle… 等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息