内存泄漏
首先先知道什么是内存泄漏
一般情况下,就是说A持有B的引用,当B需要回收但A还在被持有,被使用的时候,A没有释放这个B的引用,那么B就是可达的【因为A没有释放,可以通过A找到B】,就不会被回收,造成内存泄漏
Android中常见内存泄漏
- 静态变量引用的对象:生命周期和应用一样,一般不会被回收
- 非静态内部类:非静态内部类默认持有外部类对象,如果其他地方使用了这个非静态内部类的对象,它持有的外部类对象就是可达的,不会被回收
- Webview
- 资源未释放
使用android studio的profile进行内存泄漏分析
实际操作
- 里面有个拖动时间轴查看内存分配情况的操作,但是前提是,你链接的设备的系统是8.0或者以上的
通过多次开关页面,观察内存的波动,感觉total内存变化太大,貌似不太符合实际情况,所以进行Java部分的内存大小观察,发现Java部分的应该是反映了内存情况的。
比如我们要观察A页面是否发生了内存泄漏,或者你怀疑A页面发生了内存泄漏的话,可以在打开A页面之前,观察记录此时的内存的使用情况
然后打开A页面进行操作,操作结束后,进行GC,,GC后再观察此时的total和Java的内存使用情况,来判断你的页面是否发生了内存泄漏
点击dump java heap图表,会对当前内存进行分析
选择按包名排列,找到我们自己的包名,看里面的对象,比如此时存在A页面中的对象,说明这个对象实例还存在,我们点击它会在右边出现instance窗口,我们点击里面的实例,会在下面出现引用窗口。我们右键引用,会出现两个选项,可以帮助我们定位到代码
在 Instance View 窗格中,点击一个实例。此时下方将出现 References 标签,显示对该对象的每个引用。
或者,点击实例名称旁边的箭头以查看其所有字段,然后点击一个字段名称以查看其所有引用。如果您要查看某个字段的实例详细信息,请右键点击该字段并选择 Go to Instance。
在 References 标签中,如果您发现某个引用可能在泄漏内存,请右键点击它并选择 Go to Instance。这样会从堆转储中选择相应的实例,从而向您显示它自己的实例数据。
展开引用的箭头,查看下面有没有那个引用是在自己项目的文件中,去查看,比如
就查看 in 后面的,有没有哪个是自己的页面或者文件名称
tips
认为还有一种做法,就是比如进行一次操作会产生一个对象,如果这个对象是内存泄漏的话,我们进行重复操作,那么就会产生多个对象。我们就可以根据实例的个数来判断它是否发生了泄漏,比如我连续操作了三次,发现
我们确定了这个对象发生了内存泄漏,然后就是要找到它的引用,确定是哪个引用导致的内存泄漏。处理这个引用
处理内存泄露
内存泄露因为该被销毁的对象仍然GC可达,不能被回收,所以会导致我们的内存越来越小
处理方法就是使对象不可达,那么GC就会进行回收
我们找到了造成内存泄露的对象
那么如何避免内存泄露呢?常见方式
-
在对象需要销毁时,将其持有对象置空。
比如常见的MVP模式中,Activity中创建Presenter的时候,传递自己过去,Presenter持有Activity。 那么Activity需要销毁的时候,如果Presenter还在被使用,比如在请求数据等,Activity就不能被回收。 需要将Presenter的还在执行的任务结束,将Presenter置空
-
使用弱引用,相当于添加了一个标记,GC的时候看到这个标记,就直接回收
Java有四种引用 强引用:常用默认的 软引用:在内存不足的时候,优先回收弱引用对象 弱引用:只要进行GC,就回收弱引用对象 虚引用:虚引用与引用队列配合使用
其他工具
mat
这个文章中,提到使用hprof-conv指令,这个是sdk下的工具,在platform-tools目录下,所以我们需要进入这个目录下,才能使用这个指令。
但这个mat还是基于as的profile生成的内存情况的文件进行分析的
Leaks
- 我们可以使用Leaks来进行内存泄漏检测,关于Leaks的使用可以先参考博文
- 参考博文
我们操作App,在发生泄漏的时候,Leaks会进行记录,我们可以查看记录来排查内存泄漏
看下官方的举例的图:
- 最顶部的是一个GC root
- 最底部发生泄漏的对象
这整个图可以看到GC root是如何到达泄漏的对象的,也就是可达性的过程