HeapProfile-1-官方文档翻译


一、Quickstart: Heap profiling

注: 翻译自 Quickstart: Heap profiling: https://perfetto.dev/docs/quickstart/heap-profiling

1. 先决条件(Prerequisites)

已安装 ADB。
运行 Android 10+ 的设备。
可分析或可调试的应用。如果您在 Android 的"user"版本上运行(而不是"userdebug"或"eng"),则您的应用需要在其清单中标记为可分析或可调试。有关更多详细信息,请参阅 heapprofd 文档。//https://perfetto.dev/docs/data-sources/native-heap-profiler#heapprofd-targets

注:user版本也可以使用,只不过是要在 Manifest.xml 中配置一下 <profileable android:shell="true"/>


2. 捕获堆配置文件(Capture a heap profile)

2.1 Linux / macOS

确保 adb 已安装并位于您的 PATH 中。

adb devices -l

如果报告了多个设备或模拟器,您必须先选择一个,如下所示:

export ANDROID_SERIAL=SER123456

下载 tools/heap_profile(如果您没有 perfetto checkout):

curl -LO https://raw.githubusercontent.com/google/perfetto/main/tools/heap_profile
chmod +x heap_profile

然后启动配置文件:

./heap_profile -n system_server


2.2 Windows

确保下载的 adb.exe 在 PATH 中。

set PATH=%PATH%;%USERPROFILE%\Downloads\platform-tools
adb devices -l //-l可以看到更详细的信息

如果报告了多个设备或模拟器,则必须先选择一个,如下所示:

set ANDROID_SERIAL=SER123456

下载 heap_profile 脚本。然后启动配置文件://https://raw.githubusercontent.com/google/perfetto/main/tools/heap_profile

python /path/to/heap_profile -n system_server

注: 多个adb设备连接,一个窗口中使用 set ANDROID_SERIAL 后adb shell就有目标设备能运行了,而且不影响新开的adb shell窗口。Windows中没有export指令,只关注set即可。


3. 查看配置文件(View profile)

将原始跟踪文件从输出目录上传到 https://ui.perfetto.dev/ 然后单击 UI 轨道中标有 "Heap profile" 的菱形标记。


4. 实验-补充

8155上实验失败,报错如下,换成python3执行也是失败

114.HeapProfile>python heap_profile -n system_server
File "heap_profile", line 295
sys.exit(subprocess.check_call([bin_path, *sys.argv[1:]]))
^
SyntaxError: invalid syntax


5. 下一步(Next steps)

在 Android 内存使用指南(Memory Usage on Android Guide)中了解有关内存调试的更多信息,以及有关 heapprofd 数据源(heapprofd data-source)的更多信息
//https://perfetto.dev/docs/case-studies/memory
//https://perfetto.dev/docs/data-sources/native-heap-profiler


注:
heap_profile 脚本:https://raw.githubusercontent.com/google/perfetto/main/tools/heap_profile


二、Android 内存使用指南(Debugging memory usage on Android)

注: 翻译自 Debugging memory usage on Android: https://perfetto.dev/docs/case-studies/memory


1. 先决条件

运行 macOS 或 Linux 的主机。//Windows不行吗?
已安装 ADB 并位于 PATH 中。
运行 Android 11+ 的设备。

如果您正在分析自己的应用,并且未运行 Android 的 userdebug 版本,则需要在manifest中将您的应用标记为可分析或可调试。有关可以定位哪些应用的详细信息,请参阅 heapprofd 文档(heapprofd documentation)。
//https://perfetto.dev/docs/data-sources/native-heap-profiler#heapprofd-targets


2. dumpsys meminfo

开始调查进程内存使用情况的一个好地方是 dumpsys meminfo,它提供了进程使用各种类型内存量的高级概述。

$ adb shell dumpsys meminfo com.android.systemui

Applications Memory Usage (in Kilobytes):
Uptime: 2030149 Realtime: 2030149

** MEMINFO in pid 1974 [com.android.systemui] **
                   Pss  Private  Private  SwapPss      Rss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty    Total     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------   ------
  Native Heap    16840    16804        0     6764    19428    34024    25037     5553
  Dalvik Heap     9110     9032        0      136    13164    36444     9111    27333

[more stuff...]

查看 Dalvik Heap(=Java Heap)和 Native Heap 的 "Private Dirty" 列,我们可以看到SystemUI在Java堆上的内存使用量为9M,在本机堆上为17M。

补充: dump出来的完整信息:

# dumpsys meminfo com.android.systemui
Applications Memory Usage (in Kilobytes):
Uptime: 2174255 Realtime: 2174255

** MEMINFO in pid 1457 [com.android.systemui] **
                   Pss  Private  Private     Swap     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    17678    17628        0        0    25292    22571     2720
  Dalvik Heap     1397     1364        0        0     3341     1671     1670
 Dalvik Other      865      864        0        0
        Stack       36       36        0        0
       Ashmem       94       92        0        0
      Gfx dev       16       16        0        0
    Other dev       12        0       12        0
     .so mmap     1115       84        0        0
    .jar mmap      518        0       28        0
    .apk mmap     7524        0     7408        0
    .dex mmap      176        4      172        0
    .oat mmap      372        0        0        0
    .art mmap     1104      888        0        0
   Other mmap      160       48        0        0
    GL mtrack      192      192        0        0
      Unknown     1051     1048        0        0
        TOTAL    32310    22264     7620        0    28633    24242     4390

 App Summary
                       Pss(KB)
                        ------
           Java Heap:     2252
         Native Heap:    17628
                Code:     7696
               Stack:       36
            Graphics:      208
       Private Other:     2064
              System:     2426

               TOTAL:    32310      TOTAL SWAP (KB):        0

 Objects
               Views:        0         ViewRootImpl:        0
         AppContexts:        7           Activities:        0
              Assets:        2        AssetManagers:        0
       Local Binders:       51        Proxy Binders:       42
       Parcel memory:        7         Parcel count:       30
    Death Recipients:        0      OpenSSL Sockets:        0
            WebViews:        0

 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0


3. Linux memory management

但是 clean、dirty、Rss、Pss、Swap 到底是什么意思呢?要回答这个问题,我们需要深入研究一下 Linux 内存管理。

从内核的角度来看,内存被分成大小相等的块,称为页面。这些页面通常为 4KiB。

页面组织在称为 VMA(虚拟内存区域)的几乎连续的范围内。

当进程通过 mmap() 系统调用请求新的内存页面池时,会创建 VMA。应用程序很少直接调用 mmap()。这些调用通常由分配器、malloc()/operator new()(对于native进程)或 Android RunTime(对于 Java 应用程序)进行调用。

VMA 可以分为两种类型:基于文件的和匿名的。

文件支持的 VMA 是内存中文件的视图。它们是通过将文件描述符传递给 mmap() 获得的。内核将通过传递的文件在 VMA 上提供page-faults,因此读取指向 VMA 的指针相当于对文件执行 read()。例如,动态链接器 (ld) 在执行新进程或动态加载库时会使用文件支持的 VMA,或者 Android 框架在加载新的 .dex 库或访问 APK 中的资源时会使用文件支持的 VMA。

匿名 VMA 是仅用于内存的区域,不基于任何文件。这是分配器从内核请求动态内存的方式。匿名 VMA 是通过调用 mmap(... MAP_ANONYMOUS ...) 获得的。######

只有在应用程序尝试从 VMA 读取/写入时,才会以页面粒度分配物理内存。如果您分配了 32 MiB 的页面但只触及一个字节,则进程的内存使用量只会增加 4KiB。##### 您将进程的虚拟内存增加了 32 MiB,但其驻留物理内存增加了 4 KiB。

在优化程序的内存使用时,我们感兴趣的是减少它们在物理内存中的占用空间。#####在现代平台上,高虚拟内存使用率通常不会引起担忧(除非您用完了地址空间,这在 64 位系统上非常困难)。

我们将进程驻留在物理内存中的内存量称为 RSS(驻留集大小)。#####但并非所有驻留内存都相同。

从内存消耗的角度来看,VMA 中的各个页面可以具有以下状态:

(1) 常驻(Resident):页面被映射到物理内存页面。常驻页面可以处于两种状态:

Clean(仅适用于基于文件的页面):页面的内容与磁盘上的内容相同。在内存压力的情况下,内核可以更轻松地驱逐干净页面。这是因为如果再次需要它们,内核知道它可以通过从底层文件中读取它们来重新创建其内容。
Dirty:页面的内容与磁盘不同,或者(在大多数情况下),页面没有磁盘支持(即它是匿名的)。脏页不能被驱逐,因为这样做会导致数据丢失。但是,如果存在,它们可以交换到磁盘或 ZRAM 上。

(2) 交换(Swapped):脏页可以写入磁盘上的交换文件(在大多数 Linux 桌面发行版上)或压缩(在 Android 和 CrOS 上通过 ZRAM)。页面将保持交换状态,直到其虚拟地址发生新的page-fault,此时内核会将其带回主内存。

(3) Not present:页面上从未发生过页面错误,或者页面是干净的,后来被驱逐。

通常,减少脏内存的数量更为重要,因为脏内存无法像干净内存那样被回收,而且在 Android 上,即使将其交换到 ZRAM 中,仍会占用部分系统内存预算。#####这就是我们在 dumpsys meminfo 示例中查看 Private Dirty 的原因。

共享内存可以映射到多个进程中。这意味着不同进程中的 VMA 引用相同的物理内存。这通常发生在常用库(例如 libc.so、framework.dex)的文件支持内存中,或者更罕见的是,当进程 fork() 并且子进程从其父进程继承脏内存时。

这引入了 PSS(比例集大小)#####的概念。在 PSS 中,驻留在多个进程中的内存按比例分配给每个进程。如果我们将一个 4KiB 页面映射到四个进程中,则每个进程的 PSS 将增加 1KiB。

回顾(Recap)
动态分配的内存,无论是通过 C 的 malloc()、C++ 的运算符 new() 还是 Java 的 new X() 分配的,一开始都是匿名且脏的,除非从未使用过。

如果一段时间内未读取/写入此内存,或者内存压力过大,它会被换出到 ZRAM 并被交换。

匿名内存,无论是常驻内存(因此是脏的)还是交换内存,总是占用大量资源,如不必要应避免使用。

文件映射内存来自代码(Java 或native)、库和资源,几乎总是干净的。干净的内存也会侵蚀系统内存预算,但通常应用程序开发人员对它的控制较少。


4. 随时间变化的内存(Memory over time)

dumpsys meminfo 非常适合获取当前内存使用情况的快照,但即使是非常短暂的内存峰值也会导致内存不足的情况,从而导致 LMK。我们有两个工具可以调查此类情况: //https://perfetto.dev/docs/case-studies/memory#lmk

RSS High Watermark.

Memory tracepoints.


5. RSS High Watermark

我们可以从 /proc/[pid]/status 文件中获取大量信息,包括内存信息。VmHWM 显示进程自启动以来的最大 RSS 使用量。此值由内核不断更新。

$ cat /proc/$(pidof com.android.systemui)/status
[...]
VmHWM:    256972 kB
VmRSS:    195272 kB
RssAnon:  30184 kB
RssFile:  164420 kB
RssShmem: 668 kB
VmSwap:   43960 kB
[...]


6. Memory tracepoints

注意:有关内存跟踪点的详细说明,请参阅 Data sources > Memory > Counters and events 页面。//https://perfetto.dev/docs/data-sources/memory-counters

我们可以使用 Perfetto 从内核获取有关内存管理事件的信息。

$ adb shell perfetto \
  -c - --txt \
  -o /data/misc/perfetto-traces/trace \
<<EOF

buffers: {
    size_kb: 8960
    fill_policy: DISCARD
}
buffers: {
    size_kb: 1280
    fill_policy: DISCARD
}
data_sources: {
    config {
        name: "linux.process_stats"
        target_buffer: 1
        process_stats_config {
            scan_all_processes_on_start: true
        }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "mm_event/mm_event_record"
            ftrace_events: "kmem/rss_stat"
            ftrace_events: "kmem/ion_heap_grow"
            ftrace_events: "kmem/ion_heap_shrink"
        }
    }
}
duration_ms: 30000

EOF

在运行时,如果您正在跟踪,请拍照。

使用 adb pull /data/misc/perfetto-traces/trace ~/mem-trace 拉取文件并上传到 Perfetto UI。这将显示有关系统 ION 使用情况的总体统计信息,以及每个进程的统计信息以展开。向下滚动(或按 Ctrl-F 键)到 com.google.android.GoogleCamera 并展开。这将显示相​​机各种内存统计信息的时间线。

注:8155上实测,报错:
perfetto_cmd.cc:467 The TraceConfig is empty

我们可以看到,在跟踪的 2/3 左右,内存出现峰值(在 mem.rss.anon 轨道中)。这是我拍照的地方。这是一种查看应用程序的内存使用情况如何对不同触发器做出反应的好方法。


7. Which tool to use

如果您想要深入了解 Java 代码分配的匿名内存(由 dumpsys meminfo 标记为 Dalvik Heap),请参阅分析 java heap 部分。//https://perfetto.dev/docs/case-studies/memory#java-hprof

如果您想要深入了解本机代码分配的匿名内存(由 dumpsys meminfo 标记为 Native Heap),请参阅分析 Native Heap 部分。//https://perfetto.dev/docs/case-studies/memory#heapprofd 请注意,即使您的应用没有任何 C/C++ 代码,也经常会以native内存结束。这是因为某些框架 API(例如 Regex)的实现是通过native代码内部实现的。

如果您想深入了解文件映射内存,最好的选择是使用 adb shell showmap PID(在 Android 上)或检查 /proc/PID/smaps。######


8. Low-memory kills

当 Android 设备内存不足时,名为 lmkd 的守护进程将开始终止进程​​以释放内存。设备的策略各不相同,但一般来说,进程将按照 oom_score_adj 分数的降序顺序被终止(即首先终止后台应用和进程,最后终止前台进程)。

Android 上的应用在切换时不会被终止。相反,即使用户使用完毕,它们仍会保留在缓存中。这是为了让应用的后续启动速度更快。这类应用通常会被首先终止(因为它们的 oom_score_adj 更高)。

我们可以使用 Perfetto 收集有关 LMK 和 oom_score_adj 的信息。

$ adb shell perfetto \
  -c - --txt \
  -o /data/misc/perfetto-traces/trace \
<<EOF

buffers: {
    size_kb: 8960
    fill_policy: DISCARD
}
buffers: {
    size_kb: 1280
    fill_policy: DISCARD
}
data_sources: {
    config {
        name: "linux.process_stats"
        target_buffer: 1
        process_stats_config {
            scan_all_processes_on_start: true
        }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "lowmemorykiller/lowmemory_kill"
            ftrace_events: "oom/oom_score_adj_update"
            ftrace_events: "ftrace/print"
            atrace_apps: "lmkd"
        }
    }
}
duration_ms: 60000

EOF

使用 adb pull /data/misc/perfetto-traces/trace ~/oom-trace 拉取文件并上传到 https://ui.perfetto.dev/

我们可以看到,当相机打开时,其 OOM 分数会降低(从而降低其被关闭的可能性),而一旦关闭,其 OOM 分数会再次增加。


9. Analyzing the Native Heap

本机堆配置文件需要 Android 10。

注意:有关本机堆分析器和故障排除的详细说明,请参阅 Data sources > Heap profiler 页面。//https://perfetto.dev/docs/data-sources/native-heap-profiler

应用程序通常通过 malloc 或 C++ 的 new 获取内存,而不是直接从内核获取内存。分配器可确保您的内存得到更高效的处理(即没有太多间隙),并且询问内核的开销保持在较低水平。

我们可以使用 heapprofd 记录进程执行的本机分配和释放。生成的配置文件可用于将内存使用情况归因于特定函数调用堆栈,支持本机和 Java 代码的混合。配置文件将仅显示运行时完成的分配,不会显示之前完成的任何分配。


9.1 Capturing the profile

使用 tools/heap_profile 脚本来分析进程。如果遇到问题,请确保您使用的是最新版本。使用 tools/heap_profile -h 查看所有参数,或者使用默认值并仅分析进程(例如 system_server):

$ tools/heap_profile -n system_server

Profiling active. Press Ctrl+C to terminate.
You may disconnect your device.

Wrote profiles to /tmp/profile-1283e247-2170-4f92-8181-683763e17445 (symlink /tmp/heap_profile-latest)
These can be viewed using pprof. Googlers: head to pprof/ and upload them.

当您看到 Profiling 处于活动状态时,请稍微摆弄一下手机。完成后,按 Ctrl-C 结束配置文件。在本教程中,我打开了几个应用程序。


9.2 Viewing the data

然后将原始跟踪文件从输出目录上传到 Perfetto UI,并单击显示的菱形标记。

可用的选项卡包括:

Unreleased malloc size: 创建转储时,此调用堆栈中分配了多少字节但未释放。
Total malloc size: 此调用堆栈中分配了多少字节(包括转储时释放的字节)。
Unreleased malloc count: 此调用堆栈中进行了多少次分配而没有匹配的释放。
Total malloc count: 此调用堆栈中进行了多少次分配(包括匹配的释放)。

默认视图将显示配置文件运行时进行的所有分配但未释放的分配(空间选项卡)。

我们可以看到,通过 AssetManager.applyStyle 在路径中分配了大量内存。要获取以这种方式分配的总内存,我们可以在 Focus 文本框中输入“applyStyle”。这将仅显示某些帧与“applyStyle”匹配的调用堆栈。

由此,我们清楚地知道需要查看代码中的哪些部分。从代码中,我们可以看到内存的使用情况,以及我们是否真的需要所有内存。


10. Analyzing the Java Heap

Java 堆转储需要 Android 11

注意:有关捕获 Java 堆转储和故障排除的详细说明,请参阅 Data sources > Java heap dumps 页面。//https://perfetto.dev/docs/data-sources/java-heap-profiler


10.1 Dumping the java heap

我们可以获取构成 Java 堆的所有 Java 对象的图表快照。我们使用 tools/java_heap_dump 脚本。如果您遇到问题,请确保您使用的是最新版本。//https://raw.githubusercontent.com/google/perfetto/main/tools/java_heap_dump

$ tools/java_heap_dump -n com.android.systemui

Dumping Java Heap.
Wrote profile to /tmp/tmpup3QrQprofile
This can be viewed using https://ui.perfetto.dev.


10.2 Viewing the Data

将轨迹上传至 Perfetto UI,然后单击显示的菱形标记。

这将呈现一组火焰图视图,如下所述。

“Size”和“Objects”选项卡

注:这个有用!

这些视图显示了归因于到垃圾收集根的最短路径的内存。通常,一个对象可以通过多条路径到达,我们只显示最短的路径,因为这可以降低所显示数据的复杂性,并且通常是信号最高的路径。最右边的 "merged" 堆栈是所有太小而无法显示的对象的总和。

Size: 通过此路径到 GC 根保留了多少字节。

Size: 通过此路径到 GC 根保留了多少对象。

如果我们只想查看具有包含某些字符串的框架的调用堆栈,我们可以使用 Focus 功能。如果我们想知道与通知有关的所有分配,我们可以将“notification”放在 Focus 框中。

与本机堆配置文件一样,如果我们想关注图表的某些特定方面,我们可以按类的名称进行过滤。如果我们想查看可能由通知引起的所有内容,我们可以将“notification”放在 Focus 框中。

我们按类名聚合路径,因此如果 java.lang.Object[] 保留了多个相同类型的对象,我们将显示一个元素作为其子元素,如您在上方最左侧的堆栈中看到的那样。这也适用于如下所述的支配树路径。

"Dominated Size"主导大小和"Dominated Objects"主导对象选项卡

将堆图呈现为火焰图(树)的另一种方法是显示其支配树。在堆图中,如果对象 b 只能通过经过 a 的路径从根到达,则对象 a 支配对象 b。对象的支配者从根开始形成一个链,并且该对象由该链上的所有对象独占保留。对于图中所有可到达的对象,这些链形成一棵树,即支配树。

我们按类名聚合树路径,每个元素(树节点)代表一组在支配树中具有相同类名和位置的对象。

Dominated Size: 节点中的对象独占保留的字节数。
Dominated Objects: 节点中的对象独占保留的对象数。

 

Android 性能优化:https://source.android.com/docs/core/perf?hl=zh-cn#zram
评估效果:https://source.android.com/docs/core/tests/debug/eval_perf?hl=zh-cn
Simpleperf:https://android.googlesource.com/platform/system/extras/+/main/simpleperf/doc/README.md
CPU性能分析器: https://developer.android.com/studio/profile/cpu-profiler //即systrace
java_heap_dump.txt 下载网址:https://raw.githubusercontent.com/google/perfetto/main/tools/java_heap_dump


二、Heap profiler

注: 翻译自 Heap profiler: https://perfetto.dev/docs/data-sources/native-heap-profiler#heapprofd-targets

注意:heapprofd 需要 Android 10 或更高版本。

Heapprofd 是一种跟踪给定时间段内 Android 进程的堆分配和释放的工具。生成的配置文件可用于将内存使用情况归因于特定的调用堆栈,#####支持 native 和 Java 代码的混合。Android 平台和应用开发人员可以使用该工具来调查内存问题。

默认情况下,该工具会记录使用 malloc/free(或 new/delete)完成的native分配和释放。可以将其配置为记录 Java 堆内存分配:请参阅下面的 Java 堆采样。

在 debug Android 版本中,您可以分析所有应用和大多数系统服务。在 "user" 版本中,您只能在具有可调试或可分析清单标志的应用上使用它。

1. Quickstart

请参阅 Memory Guide 以了解如何开始使用 heapprofd。//https://perfetto.dev/docs/case-studies/memory#heapprofd


2. UI

单击菱形后,heapprofd 的转储将在 UI 中显示为火焰图。每个菱形对应于该时间点收集的分配和调用堆栈的快照。


3. SQL

有关调用堆栈的信息写入下表:

stack_profile_mapping //https://perfetto.dev/docs/analysis/sql-tables#stack_profile_mapping
stack_profile_frame //https://perfetto.dev/docs/analysis/sql-tables#stack_profile_frame
stack_profile_callsite //https://perfetto.dev/docs/analysis/sql-tables#stack_profile_callsite

分配本身写入 heap_profile_allocation。//https://perfetto.dev/docs/analysis/sql-tables#heap_profile_allocation

离线符号化数据存储在 stack_profile_symbol 中。//https://perfetto.dev/docs/analysis/sql-tables#stack_profile_symbol

有关示例 SQL 查询,请参阅 Example Queries。//https://perfetto.dev/docs/data-sources/native-heap-profiler#heapprofd-example-queries


4. Recording

Heapprofd 可以通过三种方式配置和启动。

(1) 手动配置

这需要手动设置跟踪配置的 HeapprofdConfig 部分。这样做的唯一好处是,这样可以与任何其他跟踪数据源一起启用堆分析。

(2) 使用 tools/heap_profile 脚本(推荐)

您可以使用 tools/heap_profile 脚本。如果遇到问题,请确保您使用的是最新版本。

您可以通过名称 (-n com.example.myapp) 或 PID (-p 1234) 定位进程。在第一种情况下,堆分析将在与包名称匹配的已运行进程和分析会话启动后启动的新进程上启动。有关完整参数列表,请参阅 heap_profile cmdline 参考页面。//https://perfetto.dev/docs/reference/heap_profile-cli

您可以使用 Perfetto UI 来可视化堆转储。将原始跟踪文件上传到输出目录中。您将在时间线上看到所有堆转储,它们都以菱形显示,单击其中任何一个即可获得火焰图。

或者,可以使用 Speedscope 来可视化 gzip 压缩的原型,但只会显示“未发布的 malloc 大小”视图。

(3) 使用 Perfetto UI 的录制页面

您还可以使用 Perfetto UI 来记录 heapprofd 配置文件。在跟踪配置中勾选"Heap profiling",输入要定位的进程,单击 "Add Device"以配对您的手机,然后直接从浏览器记录配置文件。这在 Windows 上也是可能的。


5. Viewing the data

对于每个菱形,生成的配置文件原型包含四个数据视图。

Unreleased malloc size: 从开始记录的时刻到菱形的时间戳,在此调用堆栈中分配了多少字节但未释放。
Total malloc size: 从开始记录的时刻到菱形的时间戳,在此调用堆栈中分配了多少字节(包括在转储时释放的字节)。
Unreleased malloc count: 从开始记录的时刻到菱形的时间戳,在此调用堆栈中进行了多少次分配而没有匹配的释放。
Total malloc count: 从开始记录的时刻到菱形的时间戳,在此调用堆栈中进行了多少次分配(包括具有匹配释放的分配)。

Google 员工:您也可以使用 http://pprof/ 打开 gzip 压缩的原型) 注:实测网站失效了

提示:在分析应用程序时,您可能希望将 libart.so 设置为“隐藏正则表达式”。

提示:单击左上角的 Left Heavy 以获得良好的可视化效果。


6. Continuous dumps

默认情况下,堆分析器会从记录开始捕获所有分配并存储单个快照,在 UI 中显示为单个菱形,该快照总结了所有分配/释放。

可以配置堆分析器定期(而不仅仅是在跟踪结束时)存储快照(连续转储),例如每 5000 毫秒

通过将 UI 中的“连续转储间隔”设置为 5000。

通过添加

continuous_dump_config {
  dump_interval_ms: 5000
}

在 HeapprofdConfig 中。
通过将 -c 5000 添加到 tools/heap_profile 的调用中。

生成的可视化结果显示多个菱形。单击每个菱形将显示从跟踪开始到该点的分配/释放摘要(即摘要是累积的)。#####


7. Sampling interval

... TODO ...


四、Memory: Java heap dumps

注: 翻译自 Memory: Java heap dumps: https://perfetto.dev/docs/data-sources/java-heap-profiler

注意:捕获 Java 堆转储需要 Android 11 或更高版本

请参阅内存指南以开始使用 Java 堆转储。

与本机堆配置文件相反,Java 堆转储报告托管对象的完整保留图,但不报告调用堆栈。Java 堆转储中记录的信息形式为:对象 X 通过其名为 Z 的类成员保留大小为 N 字节的对象 Y。

Java 堆转储不应与 Java 堆采样器获取的配置文件混淆


1. UI

单击进程的"Heap Profile"轨道中的菱形后,堆图转储将以火焰图的形式显示在 UI 中。每个菱形对应一个堆转储。

某些对象的原生大小在火焰图中表示为一个额外的子节点,前缀为“[native]”。额外的节点算作一个额外的对象。此功能仅适用于 Android 13 或更高版本。


2. SQL

有关 Java 堆的信息写入下表:

heap_graph_class
heap_graph_object
heap_graph_reference

native_size(仅适用于 Android T+)从相关的 libcore.util.NativeAllocationRegistry 中提取,不包含在 self_size 中。

例如,要获取类名使用的字节数,请运行以下查询。按原样,此查询通常会返回不可操作的信息,因为 Java 堆中的大多数字节最终都是原始数组或字符串。

select c.name, sum(o.self_size)
       from heap_graph_object o join heap_graph_class c on (o.type_id = c.id)
       where reachable = 1 group by 1 order by 2 desc;
---------------------------------------
name                sum(o.self_size)
---------------------------------------
java.lang.String    2770504
long[]              1500048
int[]               1181164
java.lang.Object[]  624812
char[]              357720
byte[]              350423
---------------------------------------

我们可以使用 experimental_flamegraph 将图规范化为树,始终采用到根的最短路径并获得累积大小。请注意,这是实验性的,API可能会发生变化。从中我们可以看到每种类型的对象占用了多少内存

为此,我们需要找到图的时间戳和upid。

select distinct graph_sample_ts, upid from heap_graph_object
graph_sample_ts upid
56785646801 1

然后我们可以使用它们来获取火焰图数据。

select name, cumulative_size
from experimental_flamegraph(
  -- The type of the profile from which the flamegraph is being generated.
  -- Always 'graph' for Java heap graphs.
  'graph',
  -- The timestamp of the heap graph sample.
  56785646801,
  -- Timestamp constraints: not relevant and always null for Java heap graphs.
  NULL,
  -- The upid of the heap graph sample.
  1,
  -- The upid group: not relevant and always null for Java heap graphs.
  NULL,
  -- A regex for focusing on a particular node in the heapgraph: for advanced
  -- use only.
  NULL
)
order by 2 desc;

...TODO...


四、heap_profile cmdline

翻译自 heap_profile cmdline: https://perfetto.dev/docs/reference/heap_profile-cli

... TODO ...

 

posted on 2024-12-14 16:58  Hello-World3  阅读(7)  评论(0编辑  收藏  举报

导航