目录
1.AS自带Monitors
2.MAT
3.TraceView
4,TraceView优化项目实战及坑
我们在做安卓性能优化时候,需要使用到大量的工具,长时间不用又不知道了,干脆总结下吧,大家使用时候也有个参考,不用到处去找
1.AS自带Monitors
当我们应用运行起来时,图中水平方向是时间轴,竖直方向是内存的分配情况,下面是时间轴
左上角工具栏三个圆圈按钮依次代表:
GC按钮 ,可以手动GC,回收程序垃圾
内存快照(Dump Java Heap) ,点击可以生成一个文件(包名+日期+“.hprof”)会自动弹出
可以记录程序一个时间段的内存情况,要点击2次,生成一个alloc文件
点这个内存快照,会在我们的AS中自动弹出一个:
HPROF Viewer查看方式
左上角两个红框,是可选列表, 分别是用来选择Heap区域, 和Class View的展示方式的.
Heap类型分为:
App Heap -- 当前App使用的Heap
Image Heap -- 磁盘上当前App的内存映射拷贝
Zygote Heap -- Zygote进程Heap(每个App进程都是从Zygote孵化出来的, 这部分基本是framework中的通用的类的Heap)
Class List View -- 类列表方式
Package Tree View -- 根据包结构的树状显示
我个人使用一般是在第一个大区域点一下任意一个,然后直接输入比如MainActivity 会搜索的。
第一个大区域是所有类的名字 里面有些属性还是解释下:
Class Name 类名,Heap中的所有Class
Total Count 内存中该类这个对象总共的数量,有的在栈中,有的在堆中
Sizeof 每个该实例占用的内存大小
Heap Count 堆内存中这个类 对象的个数
Shallow Size 所有该类的实例占用的内存大小
Retained Size 所有该类对象被释放掉,会释放多少内存
B板块右上角上角列名解释
Instance 该类的实例
Depth 深度, 从任一GC Root点到该实例的最短跳数
Dominating Size 该实例可支配的内存大小
右上角有个"的按钮, 点击会进入HPROF Analyzer的hprof的分析界面:
吧这个小箭头点开就看到泄漏的地方了
Allocation Tracker
点击下面图的另一个分析按钮
在上图中左上角有个选择怎么查看的 地方
Group by Method:用方法来分类我们的内存分配
Group by Allocator:用内存分配器来分类我们的内存分配
另外一种就是分配器的方式:
注意上面还有个 扇形图方式
形统计图是以圆心为起点,最外层是其内存实际分配的对象,每一个同心圆可能被分割成多个部分,代表了其不同的子孙,每一个同心圆代表他的一个后代,每个分割的部分代表了某一带人有多人,你双击某个同心圆中某个分割的部分,会变成以你点击的那一代为圆心再向外展开。
除了扇形图,还有柱状图可选择
2.MAT
MAT比AS自带的功能更强大,分析Java堆内存的工具,为了使用该工具,我们需要hprof文件,但是该文件不能直接被MAT使用,需要进行一步转化,可以使用hprof-conv命令来转化,但是AndroidStudio可以直接转化
下载地址:http://www.eclipse.org/mat/downloads.php
点击右键,然后选择保存的地址
用MAT打开我们导出的文件,我导出了两个文件,aaa.hprof和bbb.hprof,其中bbb.hprof是内存未泄露时的快照,bbb.hprof是内存已经泄露的快照。我们用MAT的Histogram(直方图)和Dominator Tree (支配树)来分析内存情况。Histogram可以列出内存中每个对象的名字、数量以及大小。Dominator Tree会将所有内存中的对象按大小进行排序,并且我们可以分析对象之间的引用结构。
1.选择overView
2.点击下方的histograsm
可列出每一个类的实例数。支持正则表达式查找,也可以计算出该类所有对象的retained size。默认是通过class(group by class)分类展示的
Shallow Heap
Shallow size就是对象本身占用内存的大小,不包含其引用的对象内存,实际分析中作用不大。在堆上,看起来是一堆原生的byte[], char[], int[],对象本身的内存都很小。所以我们可以看到以Shallow Heap进行排序的Histogram图中,排在第一位第二位的是byte,char
Retained Heap
Retained size是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。RetainedHeap可以更精确的反映一个对象实际占用的大小(因为如果该对象释放,retained heap都可以被释放)。
看到这个有个regex 正则的东西,在这输入我们怀疑的MainActivity在回车
可以看到MainActivity的数量是2,这不正常,解决这个,就需要查看MainActivity被谁引用了,不能被释放。我们右键选择exclude all phantom/weak/soft etc.references, 意思是查看排除虚引用/弱引用/软引用等的引用链 (这些引用最终都能够被GC干掉,所以排除)
还有其他菜单供选择
List objects with (以Dominator Tree的方式查看)
incoming references 引用到该对象的对象
outcoming references 被该对象引用的对象
Show objects by class (以class的方式查看)
incoming references 引用到该对象的对象
outcoming references 被该对象引用的对象
当你按上面操作之后,凶手就出现了
再对比看下正常的:
还有可以通过包名来查看
还有更强大的,通过OQL语句查询,有点像写SQL语句。
是不是分分钟查出MainActivity有两个对象。哈哈!
比如:查找size=0并且未使用过的ArrayList
select * from java.util.ArrayList where size=0 and modCount=0
Dominator Tree(支配树)
它可以将所有对象按照Heap大小排序显示, 这样大内存对象就是排在前几名的,我们可以搜索大内存对象通向GC Roots的路径,因为内存占用越高的对象越值得怀疑,使用方法跟Histogram(直方图)差不多
这个 我个人不是很熟悉
内存快照对比
举例一个典型的分析内存泄漏的过程:
使用 Heap查看当前堆大小为 23.00M
添加一个页后堆大小变为 23.40M
将添加的一个页删除,堆大小为 23.40M
多次操作,结果仍相似,说明添加/删除页存在内存泄漏 (也应注意排除其它因素的影响)
Dump 出操作前后的 hprof 文件 (1.hprof,2.hprof),用 mat打开,并得到 histgram结果
使用 HomePage字段过滤 histgram结果,并列出该类的对象实例列表,看到两个表中的对象集合大小不同,操作后比操作前多出一个 HomePage,说明确实存在泄漏
将两个列表进行对比,找出多出的一个对象,用查找 GC Root的方法找出是谁串起了这条引用线路,定位结束
具体参看:https://www.cnblogs.com/larack/p/6071209.html
3.traceview使用
TraceView 是 Android 平台特有的数据采集和分析工具TraceView 从代码层面分析性能问题,针对每个方法来分析,比如当我们发现我们的应用出现卡顿的时候,我们可以来分析出现卡顿时在方法的调用上有没有很耗时的操作,通过TraceView,可以得到两种数据。
单次执行最耗时的方法
执行次数最多的方法
要打开上面的面板,一般有两种方式
1、第一种方式
首先选择跟踪范围,在想要根据的代码片段之间使用以下两句代码
Debug.startMethodTracing(“hello”);
Debug.stopMethodTracing();
生成的traceview文件会自动放在SDCARD上,没有SDCARD卡会出现异常,所以使用这种方式需要确保应用的AndroidMainfest.xml中的SD卡的读写权限是打开的,其中hello是traceview文件的名字,是然后用adb导出traceview文件。
adb pull sdcard/hello.trace C:\Users\Z\Desktop
然后启动Android Device Monitor-->File-->openFile,打开traceview文件即可或者是直接将这个trace 文件拖进去。
2、第二种方式
先选择应用进程,然后点击Start Method Profiling(开启方法分析),按钮会变为Stop Method Profiling(停止方法分析),开启方法分析后,对应用的目标页面进行测试操作,测试完毕后停止方法分析,界面会自动跳转到 DDMS 的 trace 分析界面。
两种方式的对比:第一种方式更精确到方法,起点和终点都是自己定,不方便的地方是自己需要添加方法并且要导出文件,第二种方式的优缺点刚好相反。
traceView 的实战及坑
刚好要优化当前的项目,发现了之前的方案很多会出现意想不到的问题,而且有些东西 几乎是无解,网上查不到,问别人也不清楚我就遇到下面的问题:
1..trace不生成
2.生成的。trace文件不能拖到AS中,拖进来直接报错
3.在DDMS中无法打开.trace文件,
当然上面都是代码实现的方式遇到的问题,我也看了下,现在网上的博客大多数都是一样的很少有详细的讲解这个traceView的使用,以及个中方式使用的场景,更没有实际项目中使用的好文章,也可能是我看的这一百来篇博客比较少吧(主要是遇到的坑太难爬了),不废话
直接上项目:
1.下面是APPlication的代码
这个图片是我移动后的代码,没有移动前是在主线程中初始化
Stetho TinYunIniter initCoreDataUtil initServerHost 定位 清理静态变量 设置debug 模式 图片加载器 路由跳转配置信息初始化 屏幕密度相关获取
然后在子线程中:
延迟2S 七鱼 推送 友盟 EventBus
在代码配置后,运行APP,运行成功了打开cmd
进入到SD卡发现我的trace文件呢?发现不对劲,然后更换真机,我在华为mate8上,同样没有找到,这个就是一个巨坑花了我晚上下班到睡觉的时间才找到原因晚上回家 排查大概思路,继续搞,随便建一个项目配置读写内存权限,然后使用模拟器,配置运行,然后日志打印了,但是使用adb shell 进去并没有发现 .trace文件,这个时候,我就无语了,尝试找了好多traceView的使用,几乎全部都是给你讲怎么看这个图,然后是怎么个优化思路的,最后实在不行了,我想是不是读写的问题,然后在SD卡上创建一个新的txt文件 ,再用adb 命令发现还是没有,然后,我都怀疑自己的java基础是不是没写对,然后还是没有生成,回过来想想,在真机上任然还是不行,没办法了,我直接指定的在sd 卡上写文件 还是没有,最后看源码。看到底是怎么搞的,最后才搞的有点明白了,这个跟用的模拟器有关系,如果没有生成,1,检查权限,读写内存卡,2,检查你的stopTracing方法有没有执行,3,判断你的手机有没有SD卡,4,如果都没找到,就在sdcard 中有Android /data /包名/files/在这里面,为什么在这里?这个就需要看源码了最后在这里找到
现在找到了在用adb 命令吧这个pull下来
这个. trace文件挺大的,其实基本都是8M左右,现在打开AS,然后把这个trace文件拖进去,在拖进去 出现过几个异常,一个就是parse失败,另一个时空:
大致介绍下界面
另外一种就是在AS顶部的tools-android-Monitor使用DDMS打开,个人比较喜欢用这种方式,会比前面一个好看些,前面的上部分的条形图 比较小:
大致关心这么几个数据
下面方法的时间都是可以点击的,升序列降序排列,然后一般选择CPU时间的降序,那么耗时的就显而易见了。