app性能优化包括很多方面,其中重要的部分就是内存分析,主要是分析是否存在内存泄露,下面就是通过几种工具进行分析:
1、Lint
android studio自带分析工具,可以进行检测,常见的内存泄露可以检测出来
2、LeakCanary
内存泄露检测工具,只要app集成了LeakCanary,当app启动时候,出现内存泄露就会在通知栏发送通知,开发者就可以点进去查看具体的内存泄露详细信息,包括具体问题在哪一行
集成就比较简单了,只需要两部,一是在app的build.gradle中导入包
//leakCanary内存泄露检测
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
二是在application中进行初始化
/**
* LeakCanary初始化 只有在debug状态下才进行检测
* @return
*/
public void initLeakCanary(Application application) {
if (BuildConfig.DEBUG) {
LeakCanary.install(application);
}
}
3、Memory Profile
Android studio自带的分析工具,可以查看当前app的内存使用情况,包括Java或者kotlin代码的占用内存,native code占用内存,grphics图像占用内存,栈内存,以及不可定义类型占用的内存,可以实时监测内存使用情况,如果出现内存 增大或者抖动现象,可以查看占用内存的对象,使用非常方便。
4、MAT
MemoryAnalyzerTool,可以在http://www.eclipse.org/mat/downloads.php官网下载即可,下载好之后即可解压使用,点击MemoryAnalyzer.exe就可以打开界面了。
界面如下:
因为我开发用的是Android Studio工具,所以可以使用profile中的Memory导出hprof文件。
先点击dump java heap,导出堆栈信息。
然后点击保存堆栈信息,保存到一个hprof文件。
但是这个文件不能在MAT中直接打开,需要使用sdk工具进行转换一下,使用hprof-conv工具,前提是要配置sdk的环境变量。
转换之后的文件即可使用MAT打开。
打开之后的界面如下所示:
我们主要看的是内存泄露的情况,也就是在Histogram的内容。点击之后
然后我们就可以查找自己的类,是否存在内存泄露的情况。点击类右击选择List objects—》with incoming references,查找内部相互应用堆栈信息。
说明:incoming reference:是指引用我的对象列表,handler对象,Activity就是在incoming reference中。
outgoing reference:我引用的对象,比如Activity中创建的handler对象,就是在outgoing中。
结果如下:
然后右击类,点击Path To GC Root —》exclude all phantom/weak/woft etc.references ,去除弱引用之类的信息,这部分不构成内存泄露。
就能查找到泄露的信息,具体的引用参数名称和类名。
到这里我们就可以根据存在的内存泄露进行修改了。
如果存在内存泄露情况,并且LeakCanary也没有报异常,那么我们就使用Memory Profile进行监测,反复多次操作,看前后的内存使用是否存在差距,如果存在那就是存在内存泄露了,然后就根据页面分析代码。
我们也可以使用MAT对比前后两次的堆栈信息。将两次的堆栈信息,通过下面的命令,添加到对比表页面中,进行比对。
点击右边的!号,就可以生成对比的结果。
对比结果如下:然后进行筛选查找我们自己的类即可。
以上基本都能发现内存泄露问题。
说明:以上内存泄露都是通过单例传入activity对象导致的
代码如下:
/**
* 模拟内存泄露
*/
class LeakUtil private constructor(context: Context){
private var mContext:Context?=context
companion object{
private var instance :LeakUtil?=null
fun getInstance(context: Context): LeakUtil {
if (instance == null) {
synchronized(LeakUtil::class) {
if (instance == null) {
instance = LeakUtil(context)
}
}
}
return instance!!
}
}
}
Activity中调用
LeakUtil.Companion.getInstance(this);