【Android性能优化】(一) Android内存泄露分析
来源:互联网 发布:javascript if 编辑:程序博客网 时间:2024/05/17 07:05
注:转载请注明来自Nemo, http://blog.csdn.net/nemo__
一、Android Studio内存泄露查找方法
1. 在Android Studio内,按Alt+6
,跳转到Android Monitor
编辑框。
2. 关注Memory
项内容,操作怀疑内存泄露的步骤。
3. 点击Memory
右边Initiate GC
后,Dump Java Heap
,会生成一个hprof文件。
4. 可以分析内存占用较大的对象,同时在hprof编辑框内有一个Analyzer Tasks
页,右上角点击开始
按钮,会在下方列出Detect Leaked Activities
。
5. 进一步分析Reference Tree
页中,最靠前的几项,基本能定位到泄露的地方。
Activity内存泄露
问题,也可以通过adb shell dumpsys meminfo xxx.package.xxx
后,观察下方Views
及Activities
数目,是否一直在增加。
二、内存泄露原因分析
1. 慎用static关键字,注意static Context.
public class MainActivity extends Activity{ public static Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mContext = this; }}
Context对象为静态的,那么Activity就无法正常销毁,会常驻内存。
【解决方法】1.使用Application的Context;2.慎用static关键字。
2. 单例模式导致内存的泄漏
静态变量导致的内存泄漏太过明显,而单例模式带来的内存的泄漏容易被忽略。
public class SingleInstance { private static volatile SingleInstance instance; private Context context; private SingleInstance(Context context) { this.context = context; } public static SingleInstance getInstance(Context context) { if(instance == null) { synchronized(SingleInstance.class) { if(instance == null) { instance = new SingleInstance(context); } } } return instance; }}
上例中,传入给单例对象的context如果是Activity的context,而单例对象是一个static对象,其生命周期与应用程序是一致的(也就是说,只有应用程序进程被杀掉了,static对象才会被销),该SingleInstance单例静态对象持有当前Activity的context,当MainActivity退出时,由于instance还继续只有其context引用,对造成系统无法销毁该Activity,从而造成内存泄漏。
【解决方法】1.使用应用程序的getApplicationContext(),静态对象的生命周期与应用程序的生命周期一致,故此不会导致内存泄漏;2.持有传入的context的弱引用。
public class SingleInstance { private WeakReference<Context> weakContext; private SingleInstance(Context context) { weakContext = new WeakReference<Context>(context); } ....}
3. 属性动画导致的内存泄漏
从Android3.0开始,Google提供了属性动画,属性动画中有一类无限循环的动画,例:
mAnimator = ValueAnimator.ofFloat(DEGREE_START, DEGREE_END);mAnimator.setDuration(DURATION);mAnimator.setStartDelay(60);mAnimator.setRepeatCount(Animation.INFINITE);mAnimator.setRepeatMode(Animation.RESTART);mAnimator.start();
如果在Activity中播放此类动画并且在onDestroy()方法中没有停止该动画,那么动画会一直循环下去,尽管在界面上已经无法看不到动画了,但这个时候Activity的View会被动画持有,而View又持有Activity,最终Activity无法释放。由于动画是无限循环的,会泄露当前的Activity。
【解决方法】适当的时机(如onDestroy()方法中)调用Animator.cancel()方法。
if (mAnimator != null) { mAnimator.cancel();}
4. 非静态内部类持有外部类实例
public class MainActivity extends Activity { static Demo sInstance = null; @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (sInstance == null) { sInstance= new Demo(); } } class Demo { voiddoSomething() { System.out.print("doth."); } } }
第一个MainActivity activity1实例创建时,sInstance会获得并一直持有activity1的引用。
【解决方法】WeakReference引用外部实例。
public class OutterClass { ...... ...... static class InnerClass { private final WeakReference<OutterClass> mOutterClassInstance; ...... ...... final OutterClass outer = mOutterClassInstance.get(); } }
5. 注册某个对象后未反注册
【解决方法】注册广播接收器、注册观察者等,在退出时需要进行反注册。
6. 资源对象没关闭造成的内存泄露
资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于Java虚拟机内,还存在于Java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。
【解决方法】对于资源性对象在不使用的时候,应该立即调用它的close()函数,将其关闭掉,然后再置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。
7. Handler类、HandlerThread类使用不当
Handler类,Handler通过发送Message与其他线程交互,Message发出之后是存储在目标线程的MessageQueue中的,而有时候Message也不是马上就被处理的,可能会驻留比较久的时间。
在Message类中存在一个成员变量target,它强引用了handler实例,如果Message在Queue中一直存在,就会导致handler实例无法被回收,如果handler对应的类是非静态内部类,则会导致外部类实例(Activity或者Service)不会被回收,这就造成了外部类实例的泄露。
HandlerThread实现的run方法是一个无限循环,它不会自己结束,线程的生命周期超过了activity生命周期,当横竖屏切换,HandlerThread线程的数量会随着activity重建次数的增加而增加。
public classMainActivity extends Activity { @Override public void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Thread mThread = newHandlerThread("demo", Process.THREAD_PRIORITY_BACKGROUND); mThread.start(); MyHandler mHandler = new MyHandler(mThread.getLooper()); .... } @Override public void onDestroy() { super.onDestroy(); } }
【解决方法】应该在onDestroy时将线程停止掉:mThread.getLooper().quit()。对于不是HandlerThread的线程,也应该确保activity消耗后,线程已经终止,可以这样做:在onDestroy时调用mThread.join()。
8. 线程、AsyncTask类造成内存泄露
Runnable和AsyncTask都可以是一个匿名内部类,它们对当前的Activity都有一个隐式引用。如果Activity在销毁前,任务未完成,将导致Activity的内存资源无法回收。
【解决方法】同样是使用静态内部类来解决。
9. 构造Adapter时,没有使用缓存的convertView
public View getView(intposition, View convertView, ViewGroup parent)
getView()为ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的listitem。这个构造过程就是由getView()方法完成的,getView()的第二个形参convertView就是被缓存起来的listitem的view对象(初始化时缓存中没有view对象则convertView是null)。
public View getView(intposition, View convertView, ViewGroup parent) { View view = null; if (convertView != null){ view = convertView; populate(view, getItem(position)); } else { view = new Xxx(...); } return view; }
10. Bitmap使用不当
(1) 及时的销毁
系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过Java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉。
(2) 设置一定的采样率
BitmapFactory.Options options = newBitmapFactory.Options(); options.inSampleSize = 2;
(3) 运用软引用(SoftRefrence)
我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。
SoftReference<Bitmap> bitmap_ref = new SoftReference<Bitmap>(BitmapFactory.decodeStream(inputstream));....if (bitmap_ref.get() != null) { bitmap_ref.get().recycle(); }
11. 及时消除集合类引用
通常会把一些对象的引用加入到了集合中,当我们不需要该对象时,如果没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,可能更严重。长时间地使用手机才能出现OOM。
【解决方法】集合类对普通对象的引用,在不需要时要及时从集合类中消除引用。
参考:
http://blog.csdn.net/gemmem/article/details/13017999
http://www.cnblogs.com/yejiurui/archive/2013/02/23/2923418.html
http://www.kancloud.cn/digest/itfootballprefermanc/100913
- 【Android性能优化】(一) Android内存泄露分析
- Android性能优化--内存泄露
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Android 性能优化之使用MAT分析内存泄露问题
- Java学习之集合
- sqlhelper
- Mosquitto--简要教程
- js代码中 何时加入引号,何时不加
- 记录一次ceph recovery经历
- 【Android性能优化】(一) Android内存泄露分析
- Java 集合——HashMap
- 第225讲:Spark Shuffle Pluggable框架SortShuffle解析以及创建源码详解
- 793A
- 商品展示,SQLite数据库的使用
- [BZOJ 1178][Apio2009]CONVENTION会议中心:贪心+倍增
- codeforces788A Functions again
- JavaScript---对象学习(二)Array对象、Date对象及其重要函数学习
- VMware+ubuntu