Android内存泄漏
Android内存泄漏
直入正题,首先说说什么是内存泄漏
一、什么是内存泄漏
内存泄漏是当程序向系统申请内存空间后,在使用完毕后未释放内存或释放内存失败,从而产生了无用的内存消耗。各位看官也可以顺道去了解一下垃圾回收机制(gc)。这里还是简单讲一下,当一个不可到达的对象时,在执行gc操作时会被回收,而当一个我们不再使用的数据依然是可到达的状态的时候,这就是发生了内存泄漏。
二、内存泄漏的影响
在说内存泄漏的影响之前大家需要先了解一下内存溢出,简单来说,内存溢出就是所需要的内存超出了系统为之分配的内存。
而过多的内存泄漏将会导致内存溢出,这将会导致应用出现Crash,这对用户来说将会是很不好的体验,所以在开发过程中我们应该尽量避免内存泄漏的发生。
三、常见的内存泄漏情形
在开发过程中发生内存泄漏的情况很多,下面我将对比较常见的内存泄漏情况进行简单说明
1.单例模式引发的内存泄漏
Android的单例模式非常受开发者的喜爱,不过使用的不恰当的话也会造成内存泄漏。因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。
下面给个单例发生内存泄漏的例子:
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;}}
当我们在初始化getInstance的时候传入的context为Activity的context的时候将会发生内存泄漏,因为在Activity退出时,activity本该被回收,但是由于单例模式持有了Activity的引用,而单例模式的生命周期和应用一样长,所以会导致Activity不能被回收,这也就导致了内存泄漏的发生。
避免方法:用Application的Context代替Activity的Context,因为Application的生命周期和单例是相同的,所以将不会有内存泄漏问题。
2.非静态内部类引发的内存泄漏
非静态内部内部类会默认持有外部类的引用,这在内部类与外部类的生命周期不相同的时候将会发生内存泄漏。
例:
public class MainActivity extends AppCompatActivity {private static DemoResource mResource = null;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(mManager == null){ mManager = new DemoResource(); } //...}class DemoResource { //...}}
由于DemoResource是非静态内部类,持有外部类的引用,而mResource 的生命周期和应用的相同,所以当activity结束时会发生内存泄漏。
避免方法:避免使用非静态内部类,尽量使用静态内部类。
3.Handler造成的内存泄漏
在开发过程中需要访问网络或一些耗时的操作时经常会使用Handler进行处理,这时候不加留意便会发生内存泄漏。(我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用)
下面举一个Handler内存泄漏的例子:
public class MainActivity extends AppCompatActivity {private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { //... }};@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); loadData();}private void loadData(){ //...request//发送消息 Message message = Message.obtain(); mHandler.sendMessage(message);}}
这种写法相信很多同学都有过,然而这样将会造成内存泄漏,因为mHandler持有Activity的引用。在上面括号中的情况发生时会发生内存泄漏。
解决办法:
继承Handler写一个静态内部类,然后对Handler的持有对象使用弱引用,这样再回收是也可以回收handler持有的对象。并在Activity的Destroy时或者Stop时应该移除消息队列中的消息。
具体的示例如下:
public class MainActivity extends AppCompatActivity {private MyHandler mHandler = new MyHandler(this);private TextView mTextView ;private static class MyHandler extends Handler { private WeakReference 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(""); } }}@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)findViewById(R.id.textview); loadData();}private void loadData() { //...request Message message = Message.obtain(); mHandler.sendMessage(message);}@Overrideprotected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null);}}
注意,因为使用的是弱引用持有数据,所以在使用时一定要先进行判Null,否则可能会造成空指针异常
4.线程造成的内存泄漏
对于线程造成的内存泄漏,也是平时比较常见的,废话不说,直接上示例:
new Thread(new Runnable() { @Override public void run() { SystemClock.sleep(10000); //延时操作 } }).start();
上面的Runnable是一个匿名内部类,所以ta对activity有一个隐式的引用, 很明显ta进行了一个延时操作,当我们在现在还没有执行完之前关闭了这个activity,也就发生了内存泄漏。
解决办法:你都能猜到的方法,依然还是使用静态内部类。示例代码就不上了
5.资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
总结
以上是在android开发中常见的内存泄漏情形,对于生命周期不一致的问题(其实前四种都可以看作这种情况),主要是采用静态内部类,还有就是合理使用弱引用(ps:大家也可以去看看弱引用、软引用、强引用等的区别),其他具体开发中的问题还需具体分析。对于资源,一定要及时关闭
更多相关文章
- Android内存机制分析——堆和栈
- 彻底解决Android 拍照 内存溢出 Out of Memory的问题
- Android内存泄漏的轻松解决方法
- 安卓Android的内存管理原理解析
- [置顶] Android 之 内存管理