Android 常见的内存泄露
2017-08-09 17:46
441 查看
当一个对象不再需要时,本应该被回收,而被另一个正在使用的对象(可能是静态对象,也有可能是匿名内部类对象,等等)引用导致不能正常被回收,就会一直存活在堆中,造成内存泄露。内存泄露会使程序造成程序OOM(Out Of Memory),导致应用Crash。
如例子:
这样写本来没错,如果你这么用的话:
那MainActivity就别想被回收了,给我老实待在堆内存中吧!!原因是被单例AppManager所引用。改善的方法:
这样使用的话,MainActivity就不会被引用,如果没有其他地方引用MainActivity的话,等finish了,就可以等着被回收了。
因为mHandler是MainActivity的内部类,所以隐式持有MainActivity这个实例。由于Handler还有没有处理的Message,而Message持有mHandler实例,此时即使MainActivity关掉了,内存资源也无法及时被回收,造成内存泄露。解决办法:
把非静态内部类声明为静态内部类(其实就相当于单独的一个类,写在某个类里面而已),这样的话mH
4000
andler就不再直接持有MainActivity实例了。
因为要用到MainActivity实例,使用弱引用方式引用MainActivity实例,这样即使mHandler还有没处理的Message,等MainActivity关掉了,MainActivity资源也可以正常被回收。
虽然步骤1和2避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该调用mHandler的removeCallbacksAndMessages或者removeCallbacks移除消息队列中的消息。
和Handler一样,因为new出来的Thread匿名内部类也隐式持有MainActivity实例,所以会造成内存泄露,解决的办法如解决Handler内存泄露的原理一样,不再赘述。
Cursor,Stream 之类的没有 close
无限循环的动画在 Activity 退出前没有停止
一些其他的该 release 的没有 release,该 recycle 的没有 recycle… 等等。
单例造成的内存泄漏
由于单例的静态特性,使得单例对象和应用的生命周期一样长。所以被单例对象引用的对象,如果不主动置为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… 等等。
相关文章推荐
- Android 内存优化-常见内存泄露
- Android开发常见内存泄露--非静态内部类引起的内存泄露
- Android下常见的内存泄露
- Android常见内存泄露
- Android常见内存泄露,学会这六招大大优化APP性能
- Android 中常见的内存泄露
- Android下常见的内存泄露 经典
- Android常见内存泄露优化总结
- android常见的内存泄露及解决方案
- Android开发笔记——常见BUG类型之内存泄露与线程安全
- Android中常见的内存泄露
- Android常见内存泄露及优化
- [置顶] android开发中常见的规避内存泄露建议
- Android 常见内存泄露
- Android下常见的内存泄露 经典
- Android下常见的内存泄露
- Android 常见内存泄露
- Android下常见的内存泄露 经典
- Android下常见的内存泄露 经典
- Android内存优化——常见内存泄露及优化方案