常见的Android内存泄漏问题以及解决办法
来源:互联网 发布:中国原油10月进口数据 编辑:程序博客网 时间:2024/05/21 12:45
常见的Android内存泄漏问题以及解决办法
什么是内存泄漏以及其危害
编写代码的时候因为错误或者疏忽,导致一部分内存空间不能被垃圾回收机制回收,造成这部分的内存空间浪费,再也不能被程序使用到,这就叫做内存泄漏。这部分内存并不是消失不见了,而是这部分内存当中存储的数据已经不再使用,但是又占用着位置,导致物理内存空间得不到释放。
内存泄漏会导致程序可使用的内存减少,从而造成OOM(out of memory)等不利因素,造成程序崩溃。
常见内存泄漏以及解决办法
单例模式造成的内存泄漏
一般情况下,单例模式的生命周期是跟应用的生命周期一样长的,所以当单例模式中的对象持有一个需要释放的对象时,这就会导致这个需要释放的对象得不到释放,从而造成内存泄漏。比如如下例子:
class AppFactory{ private static AppFactory sAppFactory; private Context mContext; private AppFactory(Context context){ this.mContext = context; } public static AppFactory getInstance(Context context){ if(sAppFactory == null){ sAppFactory = new AppFactory(context); } return sAppFactory; }}
当context传入的是一个activity的时候,则会造成内存泄漏,因为当activity关闭的时候,因为单例对象还持有activity的引用,导致这个activity对象不能够被释放回收,从而造成浪费。
解决办法:
1、把传入的activity对象改成applicationContext
2、把上述代码改成如下:
class AppFactory{ private static AppFactory sAppFactory; private Context mContext; private AppFactory(Context context){ this.mContext = context.getApplicationContext(); } public static AppFactory getInstance(Context context){ if(sAppFactory == null){ sAppFactory = new AppFactory(context); } return sAppFactory; }}
不管传入的是什么context,最终都变成了applicationContext,这个生命周期就是应用程序的生命周期,从而避免了内存泄漏。
注: 上面标注了一个“一般情况下”,也就是正常情况下,而非正常情况就是内存不够的时候,系统会把静态对象直接置为null,所以这就不存在持有context的引用了。同时,当要持有持久性数据时,不要用static来保存这个数据。
非静态内部类造成的内存泄漏
很多时候,处于各种原因,我们都会在一个类里面创建另外的类,也就是内部类,不管匿名的还是非匿名的,都会创建一个或者多个。由于内部类会默认持有外部类的一个引用,所以当使用内部类的时候,如果内部类对象没有及时释放外部类的引用,则会造成内存泄漏。
内部类造成的内存泄漏有以下几种情况:
1、非静态内部类的静态对象
代码如下:
class MainActivity extends Activity{ private static Test sTest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(sTest == null){ sTest = new Test(); } } class Test{ }}
这里为了避免重复创建Test对象,所以使用了static关键字。但是因为静态对象的生命周期跟应用程序的生命周期是一样长的,所以当关闭这个activity的时候会造成内存泄漏。
解决办法: 将该内部类拿出来封装成一个单例,如果使用到了Context,则使用applicationContext。
2、Handler造成的内存泄漏
class MainActivity extends Activity{ private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
因为Handler是非静态内部类,所以会持有外部类的引用。但是有人会问:这个Handler对象不是静态的,这怎么也会造成内存泄漏呢?Handler里面是有个消息队列的,消息队列里能够存储很多消息,而handler处理消息是要时间的,比如在还有消息需要处理的时候,把activity关闭了,因为Handler还在处理消息,肯定不可能释放掉外部类的引用的,即activity的引用,所以这个就造成了内存泄漏。
解决办法: 首先把handler类定义成静态的,然后显示的持有外部类的引用,但是这个持有不能是强引用,而是使用弱引用,这样当回收时就可以释放外部类的引用了,代码如下:
class MainActivity extends Activity{ private MyHandler myHandler = new MyHandler(this); private static class MyHandler extends Handler { private WeakReference<Context> reference; public MyHandler(Context context) { reference = new WeakReference<>(context); } @Override public void handleMessage(Message msg) { } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
这样一改之后就可以避免activity的内存泄漏了,但是当还有消息队列里面还有消息或者正在处理最后一个消息时,虽然activity不会内存泄漏了,但是这些剩余的消息或者正在处理的消息会造成一些内存泄漏,所以最好的做法是在onstop方法或者ondestory方法里把这些消息移除掉,代码如下:
class MainActivity extends Activity{ private MyHandler myHandler = new MyHandler(this); private static class MyHandler extends Handler { private WeakReference<Activity> reference; public MyHandler(Activity activity) { reference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = reference.get(); if(activity == null){ return; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onDestroy() { super.onDestroy(); myHandler.removeCallbacksAndMessages(null); }}
使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。
3、activity中线程内部类造成内存泄漏
因为内部类会持有外部类的引用,而线程又是用来处理耗时操作的,所以当线程还在处理耗时操作时就把activity关闭了,这就会造成内存泄漏。
解决办法: 显示的持有外部类的弱引用,这个跟上面的是一样的,只是不需要移除消息队列里的消息。
资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
Bitmap使用后未释放内存造成的内存泄漏
如果Bitmap造成的内存泄漏,很大的可能会直接导致程序崩溃。所以当一个Bitmap对象不使用时,一定要调用recycle()释放内存。如果Bitmap对象没有释放,那么很容易就会出现OOM,从而导致应用程序崩溃。
集合中的对象未清理掉
我们经常把一些对象的引用加入到集合中,但是当这些对象的引用不用的时候,我们并没有把这些对象清理掉,这样会造成内存泄漏,导致集合的内存越来越大。如果这个集合是static的,那么危害更大。
ListView以及其他使用BaseAdapter的控件造成的内存泄漏
当给ListView设置Adapter的时候会继承BaseAdapter,其中有个方法是public View getView(int position, View convertView, ViewGroup parent),其中第二个参数会被缓存起来,如果在这个方法里面始终创建一个View来返回,那么这个缓存的View就浪费掉了,使用不到,从而造成内存泄漏。而且因为要重新创建一个View,这里会造成性能的消耗,即浪费资源,也浪费时间。
解决办法:判断convertView是否为空,如果为空,则创建这个View,如果不为空,则直接使用convertView,即不会造成内存泄漏,也可以提高性能。
- 常见的Android内存泄漏问题以及解决办法
- Android常见内存泄漏以及解决办法
- Android开发 |常见的内存泄漏问题及解决办法
- Android开发 |常见的内存泄漏问题及解决办法
- Android中常见的内存泄漏问题及解决办法
- Android——内存篇:Android中5种最常见的内存泄漏问题以及解决办法
- 安卓常见的内存泄漏实例以及解决办法
- 常见的Android 内存泄漏问题
- 常见的导致Android内存泄漏问题
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 5个Android开发中比较常见的内存泄漏问题及解决办法
- 观察者模式学习笔记
- preventDefault()
- android消息机制原理详解
- 【Python】学习笔记——-4.0、高级特性
- 特征工程
- 常见的Android内存泄漏问题以及解决办法
- Mac IntelliJ IDEA 创建maven-archetype-webapp超时问题
- Android-View事件体系之View的基础知识
- python安装某模块时出现 TypeError: __call__() takes exactly
- 【Python】学习笔记——-4.1、切片
- Android子线程中更新UI的3种方法
- View事件体系之View的的滑动
- intent详解(一)
- XiaomiRouter自学之路(01-项目背景)