内存泄漏面试总结

内存泄漏怎么产生的

内存泄漏的主要原因在于一个生命周期长的东西间接引用了一个生命周期短的东西,会造成生命周期短的东西无法被回收

避免内存泄漏的一些技巧

1、不要以为Java永远会帮你清理回收正在运行的threads.在上面的代码中,我们很容易误以为当Activity结束销毁时会帮我们把正在运行的thread也结束回收掉,但事情永远不是这样的!Java threads会一直存在,只有当线程运行完成或被杀死掉,线程才会被回收。所以我们应该养成为thread设置退出逻辑条件的习惯。
2、适当的考虑下是否应该使用线程.Android应用框架设计了许多的类来简化执行后台任务,我们可以使用与Activity生命周期相关联的Loaders来执行简短的后台查询任务。如果一个线程不依赖与Activity,我们还可以使用Service来执行后台任务,然后用BroadcastReceiver来向Activity报告结果。另外需要注意的是本文讨论的thread同样适用于AsyncTasks,AsyncTask同样也是由线程来实现,只不过使用了Java5.0新增并发包中的功能,但同时需要注意的是根据官方文档所说,AsyncTask适用于执行一些简短的后台任务。
频繁的使用static关键字修饰
3、很多初学者非常喜欢用static类static变量,声明赋值调用都简单方便。由于static声明变量的生命周期其实是和APP的生命周期一样的(进程级别)。大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销,直至挂掉。static的合理使用一般用来修饰基本数据类型或者轻量级对象,尽量避免修复集合或者大对象,常用作修饰全局配置项、工具类方法、内部类。
4、BitMap隐患
Bitmap的不当处理极可能造成OOM,绝大多数情况应用程序OOM都是因这个原因出现的。Bitamp位图是Android中当之无愧的胖子,所以在操作的时候必须小心。
及时释放recycle。由于Dalivk并不会主动的去回收,需要开发者在Bitmap不被使用的时候recycle掉。
设置一定的压缩率。需求允许的话,应该去对BItmap进行一定的缩放,通过BitmapFactory.Options的inSampleSize属性进行控制。如果仅仅只想获得Bitmap的属性,其实并不需要根据BItmap的像素去分配内存,只需在解析读取Bmp的时候使用BitmapFactory.Options的inJustDecodeBounds属性。
最后建议大家在加载网络图片的时候,使用软引用或者弱引用并进行本地缓存,推荐使用android-universal-imageloader或者xUtils。
引用地狱
5、Activity中生成的对象原则上是应该在Activity生命周期结束之后就释放的。Activity对象本身也是,所以应该尽量避免有appliction进程级别的对象来引用Activity级别的对象,如果有的话也应该在Activity结束的时候解引用。如不应用applicationContext在Activity中获取资源。Service也一样。
有的时候我们也会为了程序的效率性能把本来是Activity级里才用的资源提升到进程级别,比如ImageCache,或者其它DataManager等。
我只能说,空间和时间是相对的,有的时候需要牺牲时间换取空间,有的时候需要牺牲空间换取时间。内存是空间的存在,性能是时间的存在。完美的程序是在一定条件下的完美。
BroadCastReceiver、Service 解绑
绑定广播和服务,一定要记得在不需要的时候给解绑。
6、handler 清理
在Activity的onDestroy方法中调用
handler.removeCallbacksAndMessages(null);
取消所有的消息的处理,包括待处理的消息;
7、Cursor及时关闭
在查询SQLite数据库时,会返回一个Cursor,当查询完毕后,及时关闭,这样就可以把查询的结果集及时给回收掉。
8、I/O流
I/O流操作完毕,读写结束,记得关闭。
9、线程
线程不再需要继续执行的时候要记得及时关闭,开启线程数量不易过多,一般和自己机器内核数一样最好,推荐开启线程的时候,使用线程池。线程生命周期要跟activity同步。
网络请求也是线程操作的,也应该与activity生命周期同步,在onDestroy的时候cancle掉请求。

内存泄漏的检测方法

工具:
1、除了Lint外,还有像FindBugs、Checkstyle等静态代码分析工具
2、严苛模式——StrictMode StrictMode是Android系统提供的API,在开发环境下引入可以更早的暴露发现问题
在过滤日志的地方加上StrictMode的过滤Tag
3、LeakCanary
LeakCanary会弹窗提示并生成对应的堆存储信息记录,这让我们对隐蔽的内存泄漏问题有了更加直观的感觉,但从实际使用来看,LeakCanary的每个提示也并非是真正存在内存泄漏问题,要想确定是否存在问题我们还需要借助MAT来进行最后的确定。
4、Android Memory Monitor 生成的对储存信息文件可以配置MAT一起来分析使用,但是hprof文件不是标准格式,所以需要做一下转换,然后导入MAT
https://developer.android.com/studio/profile/am-hprof.html
5、Memory Analyzer (MAT)
老牌子分析工具,可以从 http://www.eclipse.org/mat/ 下载
然后通过OQL先定位出泄漏的对象,通过排除除了强引用之外的其他引用链,最后分析到GC Root的位置

两个最常用的语句:
select * from instanceof android.app.Activity
select * from instanceof android.support.v4.app.Fragment

6、adb shell命令
adb shell dumpsys meminfo [PackageName],可以打印出指定包名的应用内存信息
http://adbshell.com/
https://github.com/mzlogin/awesome-adb

LeakCanary 原理

主要分为如下7个步骤:

1、RefWatcher.watch()创建了一个KeyedWeakReference用于去观察对象。
2、然后,在后台线程中,它会检测引用是否被清除了,并且是否没有触发GC。
3、如果引用仍然没有被清除,那么它将会把堆栈信息保存在文件系统中的.hprof文件里。
4、HeapAnalyzerService被开启在一个独立的进程中,并且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap dump。
5、从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,并且定位了泄露的引用。
6、HeapAnalyzer为了确定是否有泄露,计算了到GC Roots的最短强引用路径,然后建立了导致泄露的链式引用。
7、这个结果被传回到app进程中的DisplayLeakService,然后一个泄露通知便展现出来了。

官方的原理简单来解释就是这样的:
在一个Activity执行完onDestroy()之后,将它放入WeakReference中,然后将这个WeakReference类型的Activity对象与ReferenceQueque关联。这时再从ReferenceQueque中查看是否有没有该对象,如果没有,执行gc,再次查看,还是没有的话则判断发生内存泄露了。最后用HAHA这个开源库去分析dump之后的heap内存。

参考文献:https://zhuanlan.zhihu.com/p/73675401

重点说一下watch方法吧:
watch里面只是执行了一定的准备工作,如判空(checkNotNull), 为每个引用生成一个唯一的key, 初始化KeyedWeakReference;
关键代码还是在watchExecutor中异步执行。引用检测是在异步执行的,因此这个过程不会阻塞线程。

ensureGone(KeyedReference reference,long watchStartNanoTime)
1) 移除不可达引用,如果当前引用不存在了,则不继续执行
2) 手动触发GC操作,gcTrigger中封装了gc操作的代码
3) 再次移除不可达引用,如果引用不存在了,则不继续执行
4) 如果两次判定都没有被回收,则开始分析这个引用,最终生成HeapDump信息

总结一下原理:

  1. 弱引用与ReferenceQueue联合使用,如果弱引用关联的对象被回收,则会把这个弱引用加入到ReferenceQueue中;
    通过这个原理,可以看出removeWeaklyReachableReferences()执行后,会对应删除KeyedWeakReference的数据。如果这个引用继续存在,那么就说明没有被回收。

  2. 为了确保最大保险的判定是否被回收,一共执行了两次回收判定,包括一次手动GC后的回收判定。两次都没有被回收,很大程度上说明了这个对象的内存被泄漏了,但并不能100%保证;因此LeakCanary是存在极小程度的误差的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕容野野

需要你的肯定

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值