AndroidStudio分析工具

内存监控(Memory Monitor)

AndroidStudio里的内存监控能实时的看到APP的内存情况,有助于分析内存问题。
具体用途包括:

  1. 实时查看APP的内存分配情况
  2. 判断APP卡顿是否由于GC操作引起(当然,也可以卡顿的时候仔细观察Log,是否有大量GC日志)
  3. 判断App的Crash是否由内存溢出引起

主面板

memory_monitor
这张图中:

  1. 横坐标:时间
  2. 纵坐标:分配给APP使用的内存总量[Allocated+Free]

    • 蓝色区域:分配给APP的内存中已经使用的部分[Allocated]
    • 灰色区域:分配给APP的内存中未使用处于空闲状态的部分[Free]

Android为了能同时让较多的进程常驻内存,迫使每一个进程只能使用较小的内存。
这样做的目的是使程序启动的时候就不用每次都重新加载到内存,以便更迅速响应用户操作。
出于这个设计,Android系统对dalvik的vm heapsize作了硬性限制,当java进程申请的java空间超过阈值时,就会抛出OOM异常。
这个heap大小限制可以通过下面这个命令查看:

1
adb shell getprop | grep dalvik.vm.heapgrowthlimit

在实际APP运行过程中,虚拟机会动态调整当前的APP的heap大小,当前分配的heap就由[Allocated]和[Free]组成。
顺便回顾一下内存知识:

  1. Stack空间(进栈和出栈)由操作系统控制,其中主要存储函数地址、函数参数、局部变量等等,所以Stack空间不需要很大,一般为几MB大小。

  2. Heap空间的使用由程序员控制,程序员可以使用malloc、new、free、delete等函数调用来操作这片地址空间。Heap为程序完成各种复杂任务提供内存空间,所以空间比较大,一般为几百MB到几GB。正是因为Heap空间由程序员管理,所以容易出现使用不当导致严重问题,我们平时碰到的内存问题基本上都是在heap中出现的。

GC

memory_monitor_gc
如果内存短时间发生了迅速回落,我们就认为出现了GC。
也可以通过点击左边的“小车”按钮手动GC。

用途

Memory Monitor本质上是一种监控工具,主要用途是发现问题,主要可以发现这些问题:

  1. 内存抖动
  2. 大内存对象分配
  3. 内存不断增长
  4. 卡顿是否由GC引起

memory_monitor_usage
上面第一段内存突增,就要看看是分配了什么对象,很可能是bitmap。
第二段出现了明显的内存抖动,短时间发生了多次的内存分配和释放。这个时候能明显感受到卡顿,就是高频GC引起的。
对于内存泄露,如果泄露对象比较大的话,也可以通过Memory Monitor观察出,但如果泄漏点比较小的话,还是Heap Viewer比较好用。

Heap监控(Heap Viewer)

Heap Viewer不是Android Studio新增的工具,是DDMS中一直有的一个内存监控工具。
Heap Viewer主要用于:

  1. 实时查看APP分配的内存大小和空闲内存大小(这个Memory Monitor也可以)
  2. 发现内存泄露

主面板

heap_viewer
按照图中的A->B->C->D->E顺序按下,就可以看到heap数据了。
数据随着每次GC(无论系统触发还是手动触发)都更新一次
Heap Viewer中的信息不做过多解释,都很明显。

排查内存泄露

隔一段时间就手动GC,然后观察data object一栏的total size(也可以观察Heap Size/Allocated内存的情况),看看内存是不是会回到一个稳定值,多次操作后,只要内存是稳定在某个值,那么说明没有内存溢出的,如果发现内存在每次GC后,都在增长,不管是慢增长还是快速增长,都说明有内存泄漏的可能性。

内存分配追踪(Allocation Tracker)

Allocation Tracker主要记录了在一段时间内,对APP操作引起的所有内存分配活动。
AndroidStudio的Allocation Tracker比之前Eclipse的更强大炫酷,但实现的功能基本是一样的。

操作步骤

allocation_tracker_steps

  1. 连接设备,打开调试环境,打开AndroidStudio里面的Android窗口。
  2. 点击左边的Start Allocation Tracker按钮。
  3. 操作APP。
  4. 点击左边的Stop Allocation Tracker按钮。

查看数据

上述的操作完成后,就会生成一个alloc文件,该文件记录了这次内存分配追踪到的所有数据。
有两种方式去查看具体数据。
allocation_tracer_group

按方法分组(Group by Method)

默认是Group by Method的。
group_by_method_1
可以看到,是按照线程分类的,默认按照分配顺序排序,也可以通过点击countsize来改变顺序。
count就是分配的次数,size就是分配的内存大小。
点击线程可以看到这个线程所有的涉及内存分配的方法。
group_by_method_1
并可以一步一步到底。
group_by_method_1

按内存分配器分组(Group by Allocator)

换成Group by Allocator后,详细信息就会以包来分组了。默认的排序是按照分配者的第一次分配顺序来的。
group_by_allocator_1
这种显示方式的话,我们可以直接定位到我们的代码处,看到我们代码内部的内存分配情况。
group_by_allocator_2

Jump To Source

在你感兴趣的地方,可以右键选择“Jump To Source”或者点击“Jump To Source”按钮,这个时候就会跳转到相应的源码,前提是能找到源码。
不过要注意,只能跳转到类的开头,并不跳转到具体代码行
jump_to_source

统计图

点击统计图表按钮,就进入了最为炫酷的统计图界面。
statistics_pic

轮胎图

statistics_sun_burst_1

起始分配

最内部的圆环按照线程分割,在此基础上每个扇面都是该线程的内存分配路径。
statistics_sun_burst_2

查看扇面

顺着一个扇面向外滑动,就能看到一个线程里面的内存按顺序分配的详细信息。
如果觉得不够清晰,就可以双击某个扇面,展开该扇面的相信信息。
statistics_sun_burst_3
如果想回到刚才的圆,双击圆心空白即可。
statistics_sun_burst_4

演示查看完整路径

statistics_sun_burst_5
statistics_sun_burst_6

柱状图

将查看方式由默认的Sunburst改为layout,就会展示柱状图。
statistics_change_layout
本质上没有什么区别,只是展示的样式不同。
柱状图以左边为起始点,照例以线程为单位分割,从左到右的顺序是某个的堆栈信息顺序,纵坐标上的宽度是以其Count/Size的大小决定的。
statistics_layout_1

查看某个线程堆栈路径

statistics_layout_2

查看某个分支

statistics_layout_3

Count/Size切换

statistics_layout_4

Heap快照(Heap Snapshot)

排查内存泄露最常用的方式就是去获取Heap快照,查看Java堆内存的详细信息。

Dump Heap

Android Monitor > Memory > Dump Java Heap
可以获取当前的Heap快照,并在Android Studio中显示出来。
dump_java_heap

详细信息

heap_snapshot

A区域

列举了堆内存中所有的类。

名称 意义
Total Count 内存中该类的对象个数
Heap Count 堆内存中该类的对象个数
Sizeof 物理大小
Shallow size 该对象本身占有内存大小
Retained Size 释放该对象后,节省的内存大小
B区域

当我们点击某个类时,右边的B区域会显示该类的实例化对象,这里面会显示有多少个实体,以及详细信息。

名称 意义
depth 深度
Shallow Size 对象本身内存大小
Dominating Size 管辖的内存大小

当你点击某个对象时,将展开该对象内部含有哪些对象,同时C区域也会显示哪些对象引用了该对象:
heap_snapshot_b

C区域

对象的引用树,在这里面能看出其被谁引用了。
heap_snapshot_c
在内存泄漏中,可以看出来它被谁引用。
比如上图,引用树的第一行,可以看出来,该对象被Object[12]对象引用,索引值为1,那我们展开后,可以看到,该Object[12]是一个ArrayList。
heap_snapshot_c_2

当然,内存泄露排查最好的工具还是MAT,以后我们会专门讲。