java final 内存泄露_腾讯大神教你如何解决 Android 内存泄露

20170925082241_58.png

经过长期迭代、app出现了占用内存过高的问题,上半年抽时间做了次内存泄露的分析和处理,把app占用内存从70多M降低到30多M,这里做下总结。

修复前

修复后

分析

20170925082241_552.jpg

这里主要是针对业务实际代码进行一下分析和处理,下面会总结实际使用到的一些知识。

什么是内存泄露?

1. java中的内存分配

1.静态存储区:编译时就分配好,在程序整个运行期间都存在,它主要用于存放静态数据和常量;

2.栈区:当方法执行时,会在栈区内存中创建方法内部的局部变量,方法结束后自动释放内存;

3.堆区:通常存放new出来的对象。由java垃圾会收器进行回收。内存泄露一般是指堆内存的泄露。

2.如何识别对象无引用?

1.引用计数法:直接计数,简单高效,Python便是采用该方法。但是如果出现两个对象互相引用,即时它们都无法被外界访问到,计数器不为0,它们也得不到回收。

为了解决这种问题,java采用的是第二种方法。

2.可达性分析法,这个方法设置了一系列的“GC Roots”对象作为索引七点,如果一个对象与起点对象之间均无可达路径,那么这个不可达的对象就会成为回收对象。

这种方法处理两个对象相互引用的问题,如果两个对象均没有外部引用,那么就会被判断为不可达对象进而被回收。

20170925082241_393.jpg

这边可以被当做GC roots的对象主要有以下:

android给出的解析

参考https://developer.android.com/studio/profile/am-memory.html?utm_source=android-studio

java给出的解析

参考http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3

Garbage Collection Roots

A garbage collection root is an object that is accessible from outside the heap. The following reasons make an object a GC root:

1.System Class

Class loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* .

2.JNI Local

Local variable in native code, such as user defined JNI code or JVM internal code.

3.JNI Global

Global variable in native code, such as user defined JNI code or JVM internal code.

4.Thread Block

Object referred to from a currently active thread block.

5.Thread

A started, but not stopped, thread.

6.Busy Monitor

Everything that has called wait() or notify() or that is synchronized. For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means object.

7.Java Local

Local variable. For example, input parameters or locally created objects of methods that are still in the stack of a thread.

8.Native Stack

In or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.

9.Finalizable

An object which is in a queue awaiting its finalizer to be run.

9.Unfinalized

An object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.

9.Unreachable

An object which is unreachable from any other root, but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.

10.Java Stack Frame

A Java stack frame, holding local variables. Only generated when the dump is parsed with the preference set to treat Java stack frames as objects.

11.Unknown

An object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. For these dumps the MAT parser marks objects which are have no inbound references or are unreachable from any other root as roots of this type. This ensures that MAT retains all the objects in the dump.

总结下:当前运行线程持有的对象;已经开启但是没有终止运行的线程;方法运行时的引用参数(被栈引用的堆对象);native方法中引用的对象;静态变量和方法;bootstrap loader加载的class对象;

3.内存泄漏

对于保持着引用,但是逻辑上不会再用到的对象,垃圾回收器是不会进行回收的。这些对象会累积在内存中,直到程序结束。这就是所谓的内存泄露。

如何发现内存泄漏

传统手工方法:

反复操作应用,观测内存变化,gc后内存是否有回落到原先的值;比较粗略,难发现问题。

通过代码检测:

在程序中启动长期工作的后台线程LeakThread进行内存检测

向application注册页面生命周期的监听

在监听类中对 onActivityDestoryed(Activity activity) 的事件回调做处理:在activity被调用onDestroy的时候,创建该activity的weak reference对象ref

LeakThread每隔段时间就去检测该ref.get()是否为空,如果为空则说明正常释放,不为空就手动触发gc,再隔断时间去查看是否已被回收。如果没有就dump heap,进而分析并计算到gc roots的最短路径,判断是否有泄露,如果有,就通知出来。

已经有实现的开源检测工具LeakCanary:https://github.com/square/leakcanary 强烈推荐!目前我们项目已经集成并使用了。

参考:http://km.oa.com/group/15137/articles/show/295872?kmref=search&from_page=2&no=7

如何定位内存泄漏

1.如何导出.prof文件?

如何取得内存快照,生成prof文件

20170925082241_712.jpg

2.如何分析.prof文件?

studio自带的内存泄露分析工具

20170925082241_209.jpg

导出该prof文件,生成MAT可分析的格式。

使用指令:hprof-conv input.hprof output.hprof

ABLEOU-MC4:~ ableou$ cd /Users/ableou/Downloads/adt-bundle-mac-x86_64-20140321\ 2/sdk/tools/

ABLEOU-MC4:tools ableou$ hprof-conv /Users/ableou/work/AS_ZhongCai500/captures/com.zhongcai500_2017.06.29_15.24.hprof /Users/ableou/work/AS_ZhongCai500/captures/com.zhongcai500_out_2.hprof

接下来,对转换后的prof文件,使用MTA分析

3.使用MAT进行分析

参考网站:http://androidperformance.com/2015/04/11/AndroidMemory-Usage-Of-MAT.html

常见泄露举例

1.CONTEXT_LEAK

持有activity对象不释放

让在整个程序运行过程中不会消亡的对象(如 单例对象)持有activity,可以参看initActivity之前的代码

非静态内部类持有外部类对象,并做耗时操作

延时处理的handler中持有的对象

解决:

1.单例对象尽量使用applicationContext

2.如果必须在单例对象中使用activity,则定义接口,让activity实现该接口,并通过接口使用,在activity destroy的时候需要主动触发回收。

3.修改为持有弱引用

4.在activity onDestroy之前把不需要处理的回调进行关闭。

20170925082241_807.jpg

2.RESOURCE_LEAK

我们代码主要存在:输入输出流没关闭

20170925082241_778.jpg

20170925082241_391.jpg

还有cursor没关闭、注册监听没有反注册等等。

如果想看更多可以参考:

http://km.oa.com/group/20528/articles/show/186773?kmref=search&from_page=1&no=10

https://zhuanlan.zhihu.com/p/25213586

小结:知道了泄露的原理和发现泄露的方法之后,解决泄露就不成为一个问题了。

20170925082242_419.gif

END

20170925082242_543.jpg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值