Android Studio Profiler 三种性能监测工具:Memory Profiler、CPU Profiler、Network Profiler。简单记录下这些工具的使用:
Memory Profiler
用来监测 App 内存使用情况,判断是否有内存泄漏。如图:
A 处是手动触发 GC 操作,B处 Dump Java heap,获取当前的堆栈信息,生成.hprof文件。
Record 可以记录某段时间的内存分配情况。
通过与应用交互并在Memory Profiler中观察它是如何影响内存的使用,图表可以为你展示一些潜在的问题:
- 频繁的垃圾收集活动使应用运行缓慢
- 应用耗尽内存导致app崩溃
- 潜在的内存泄漏
正常情况下,上图中的D区域会随着时间的走势慢慢上升(就算你与APP没有任何交互),直到E区域被用完,则会触发GC操作,释放内存,周而复始。如果你发现你的应用是静态的,但是E区域的内存很快就被用完了,即频繁的触发GC操作,这时你就应该引起重视,说不定你的代码中就存在着引起内存泄漏的隐患。
内存计算指标
根据Android系统,你在内存分析器的顶部看到的数字(如上图红线部分)基于你的应用所提交的所有私有内存页面。此计数不包括与系统或其他应用程序共享的页。
内存类别如下:
- Java:从Java或Kotlin代码中分配的对象的内存
- Native:从C或c++代码中分配的对象的内存,即使你没有在app中使用c++,你可能会看到一些本地内存,因为Android框架使用Native内存来处理各种任务,比如处理图像资产和其他图形——即使你写的代码是Java或Kotlin
- Graphics:用于图形缓冲区队列的内存用于显示屏幕上的像素,包括GL表面、GL纹理等。(注意,这是与CPU共享的内存,而不是专用的GPU内存)
- Stack:在你的应用程序中,Native和Java栈使用的内存。这通常与你的应用程序运行的线程数有关
- Code:您的应用程序用于代码和资源的内存,如dex字节码,优化或编译的dex代码。所以库和字体
- Other:应用程序使用的内存,系统不确定如何分类
- Allocated:应用程序分配的Java/Kotlin对象的数量。这并不计算用C或c++分配的对象
Dump Java Heap
使用场景:定位内存泄漏
点击上图中的 B 按钮开始检测 APP,此时 APP 会变得很卡,容易发生 ANR,一段时间过后会生成 .hprof 文件,如下图所示:
在 class 中:
- Allocations:该类在堆中分配的数量;
- Shallow Size:所有该类的实例占用的内存大小;
- Retained Size:该类实例可支配的内存大小,是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,释放该对象后,节省的内存大小。
选中当前列表的某一个类,如下图:
在 instance 中:
- Depth:GC Roots 点到该实例的最短链路数;
- Shallow Size:单个实例占用的内存大小;
- Retained Size:该实例可支配的内存大小。
heap dump 保存为 HPROF
如果你需要从Android HPROF文件格式转换为Java SE HPROF格式,可以使用hprof-conv工具进行转化,它的位置在 android_sdk/platform-tools/目录下,运行hprof-conv命令
1 | hprof-conv heap-original.hprof heap-converted.hprof |
GC Roots
对象存活的判定:
当一个对象不会再被使用的时候,我们会说这对象已经死亡。对象何时死亡,写程序的人应当是最清楚的。如果计算机也要弄清楚这件事情,就需要使用一些方法来进行对象存活判定,常见的方法有引用计数(Reference Counting)和可达性分析(Reachability Analysis)两种。
引用计数算法的大致思想是给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
Java语言里面没有选用引用计数算法来管理内存,其中最主要原因是它没有一个优雅的方案解决对象之间相互循环引用的问题:
当两个对象互相引用,即使它们都无法被外界使用时,它们的引用计数器也不会为0。
可达性算法的基本思路就是通过一系列的称为GC根节点(GC Roots)的对象作为起始点,从这些节点开始进行向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
CPU Profiler
Network Profiler
原文解释:
Alloc Count: Number of allocations in the heap.
Native Size: Total amount of native memory used by this object type (in bytes). This column is visible only for Android 7.0 and higher.
You will see memory here for some objects allocated in Java because Android uses native memory for some framework classes, such as Bitmap.Shallow Size: Total amount of Java memory used by this object type (in bytes).
Retained Size: Total size of memory being retained due to all instances of this class (in bytes).
At the top of the class list, you can use the left drop-down list to switch between the following heap dumps:Default heap: When no heap is specified by the system.
App heap: The primary heap on which your app allocates memory.
Image heap: The system boot image, containing classes that are preloaded during boot time. Allocations here are guaranteed to never move or go away.
Zygote heap: The copy-on-write heap where an app process is forked from in the Android system.
The list of objects in the heap are arranged by class name, by default. You can use the other drop-down to switch between the following arrangements:Arrange by class: Groups all allocations based on class name.
Arrange by package: Groups all allocations based on package name.
Arrange by callstack: Groups all allocations into their corresponding call stack. This option works only if you capture the heap dump while recording allocations. Even so, there are likely to be objects in the heap that were allocated before you started recording, so those allocations appear first, simply listed by class name.
Depth: The shortest number of hops from any GC root to the selected instance.
Native Size: Size of this instance in native memory. This column is visible only for Android 7.0 and higher.
Shallow Size: Size of this instance in Java memory.
Retained Size: Size of memory that this instance dominates (as per the dominator tree).