ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Perfetto ART Heap Dump 深度指南:捕获 Java/Kotlin 堆引用图并分析内存泄漏

Perfetto ART Heap Dump 深度指南:捕获 Java/Kotlin 堆引用图并分析内存泄漏 Perfetto ART Heap Dump 深度指南捕获 Java/Kotlin 堆引用图并分析内存泄漏【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto本指南以 Perfetto 仓库中的 docs/data-sources/java-heap-profiler.md 为骨架系统讲解 ARTAndroid RunTime堆转储Heap Dump这一数据源的完整使用链路从捕获配置、UI 火焰图解读到 trace_processor 的 SQL 表结构与 stdlib 标准库分析模块。读完本文你将掌握如何在 Android 11 设备上为指定进程抓取 Java 堆的完整引用图并用 SQL 定位真正留住内存的对象类型。ART Heap Dump 是什么引用图而非对象数据捕获 Heap Dump 需要 Android 11 或更高版本。Perfetto 的 ART Heap Dump 与标准 JVM / HPROF 堆转储有本质区别它只包含对象之间的引用图reference graph而不包含对象内部的数据内容。换句话说一次 dump 记录下来的信息形如对象 X 通过其名为 Z 的类成员保留了对象 YY 的大小为 N 字节但不会携带字符串的实际值、数组的元素内容等数据。这与 native heap profiler原生堆分析恰好互补维度ART Heap DumpNative Heap Profile记录内容Java 对象完整保留图retention graph分配事件 / 调用栈是否有调用栈无有典型用途内存泄漏定位、对象留存分析分配热点、调用路径分析Heap Dump 也不要与 ART Allocation Profiling 混淆——后者记录的是分配事件与调用栈详见 native-heap-profiler.md 中的 ART Allocation Profiling 一节。在动手之前建议先阅读 内存案例分析指南了解如何用dumpsys meminfo做高层内存概览、理解 Linux 内存管理RSS / PSS / dirty page 等再深入堆图分析。捕获 Heap DumpTraceConfig 配置详解ART heap dump 数据源通过 trace config 中的JavaHprofConfig段配置完整字段定义见源码 protos/perfetto/config/profiling/java_hprof_config.proto。原文档给出的最小可用配置如下data_sources { config { name: android.java_hprof java_hprof_config { process_cmdline: com.google.android.inputmethod.latin dump_smaps: true } } }process_cmdline进程匹配的语义演进process_cmdline是命令行的白名单按/proc/pid/cmdline注意不是 comm 字符串匹配。它的匹配语义随 Android 版本变化Android 13T字段支持单个通配符*有两种匹配方式模式以/开头时与 cmdline 的第一个段即 argv0匹配例如/bin/e*可匹配/bin/echo否则与 argv0 中对应二进制名的部分匹配与/proc/pid/exe无关例如echo可匹配/bin/echo。Android 12S及以下模式与/proc/pid/cmdline在比较前都会被归一化先裁剪掉第一个 null 或字节之后的内容若字符串含斜杠则裁剪到最后一个斜杠为止即只保留二进制名。实现层面的一个细节无论哪种方式cmdline 最多只考虑 511 个字符。其他常用字段pid按 PID 直接指定目标适用于基于水印触发或本地调试。target_installed_byAndroid 12仅分析由给定包安装的目标应用特殊值包括system系统分区、productproduct 分区、null侧载 sideload。continuous_dump_config按固定间隔连续 dump。内部包含三个子字段dump_phase_ms第一次连续 dump 前的等待毫秒数trace 开始时总会先 dump 一次dump_interval_ms后续每次 dump 的间隔毫秒数不为 0 时启用连续 dump 配置scan_pids_only_on_start为 true 时仅在数据源启动时扫描所有进程来匹配process_cmdline并按min_anonymous_memory_kb过滤Android S- 的默认行为为 false 时每次 dump 都重新扫描Android T 的默认行为。min_anonymous_memory_kb匿名 RSS swap 小于该值的进程不分析。min_java_heap_size_kbJava 堆大小小于该值的进程不分析。dump_smaps是否包含进程的/proc/self/smaps。注意只展示满足以下条件之一的映射以/system开头、以/vendor开头、以/data/app开头或包含 extracted in memory from Y其中 Y 匹配以上任一前缀。在 perfetto v58 中被smaps_config取代。smaps_configperfetto v58 新增更精细的 smaps 内存映射配置。ignored_types从 profile 中排除指定类型例如排除大量无意义的sun.misc.Cleaner对象。dump_oome_callstack仅对 OutOfMemoryError 触发的 heap dump 生效转储触发 OOM 的线程调用点。命令行快速捕获除了手工构造 TraceConfig仓库还提供了开箱即用的脚本 tools/java_heap_dump使用说明见 docs/case-studies/memory.md$ 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.另外当应用分配开始失败时也可以在 本地 Android trace 录制 场景下采集 OOM 时的对象图快照。UI 查看从 Diamond 到火焰图抓取到的 heap dump 会作为进程 track 中的ART heap dump轨道展示每次 dump 对应一个菱形标记diamond点击菱形后UI 会呈现一组火焰图视图Object Size 与 Object Count 标签页这两个视图展示的是到 GC 根最短路径上归属的内存。一个对象通常被多条路径可达只展示最短路径是为了降低数据复杂度并保留最高信号量最右侧的(merged)栈是太小而无法单独展示的对象的总和。Object Size经由此路径到 GC 根被保留的字节数。Object Count经由此路径到 GC 根被保留的对象个数。可以用 Filters 框做类名过滤例如输入notification只看与通知相关的所有分配。路径按类名聚合若多个同类对象被同一个java.lang.Object[]保留则只展示一个子元素。Dominated Object Size 与 Dominated Object Count 标签页这是将堆图按**支配树dominator tree**重新组织后的火焰图表示。在堆图中对象a支配对象b当且仅当b从根可达的所有路径都必须经过a。一个对象的所有支配者构成从根出发的一条链该对象被这条链上的所有对象排他性保留所有可达对象的这些链构成一棵树即支配树。树路径同样按类名聚合每个节点代表一组类名相同、在支配树中位置相同的对象。Dominated Object Size节点中对象排他性保留的字节数。Dominated Object Count节点中对象排他性保留的对象个数。[native] 子节点原生内存归属某些对象的原生大小native size会在火焰图中以额外子节点呈现节点前缀为[native]该额外节点会被计为额外一个对象。此能力仅 Android 13 及以上可用数据来自libcore.util.NativeAllocationRegistry且不计入self_size。SQL 分析三张核心表Java 堆信息被写入 trace_processor 的三张表表结构定义见 src/trace_processor/tables/profiler_tables.py 中的 ART Heap Graphs 分组heap_graph_class列说明name类名可能被混淆deobfuscated_name若类名被混淆且提供了反混淆映射则为反混淆后的名称location类所在的 APK / Dex / JAR 文件superclass_id父类指向本表的 idclassloader_id类加载器 idkind类种类heap_graph_object同一(upid, graph_sample_ts)的所有行构成一次 dump列说明upid目标进程的唯一 PIDgraph_sample_ts本次 dump 的时间戳self_size该对象在 Java 堆上占用的字节数native_size该对象关联的原生内存近似值来自libcore.util.NativeAllocationRegistry.sizereference_set_id与heap_graph_reference的 join key包含该对象字段引用的所有对象reachable该对象是否从 GC 根可达为 false 表示是未被回收的垃圾heap_type对象所在的 ART 堆类型app、zygote、boot imagetype_id该对象实例所属的类指向 heap_graph_classroot_type非空表示该对象是一个 GC 根root_distance到根的距离隐藏列object_data_id可选的 HPROF 原始字段值与数组数据的 ID指向 heap_graph_object_dataheap_graph_reference列说明reference_set_id引用集合 IDjoin keyowner_id引用持有者对象 idowned_id被引用对象 idfield_name字段名field_type_name字段类型名deobfuscated_field_name反混淆后的字段名实例查询 1按类名统计字节数原文档给出了第一个实用查询——统计每个类名占用的字节数。正如文档所提醒的该查询直接运行常常得不到可操作的信息因为 Java 堆中的大部分字节最终落在原始数组primitive arrays和字符串上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;结果示意namesum(o.self_size)java.lang.String2770504long[]1500048int[]1181164java.lang.Object[]624812char[]357720byte[]350423实例查询 2标准库 class_summary_tree 模块更有价值的做法是利用标准库stdlib将堆图归一化为一棵树——总是取到根的最短路径并计算累计大小从而看出每类对象到底留住了多少内存。原文档给出的用法INCLUDE PERFETTO MODULE android.memory.heap_graph.class_summary_tree; SELECT -- The name of the class. name, -- The sum of self_size of this node and all descendants of this node. cumulative_size FROM android_heap_graph_class_summary_tree;结果示意namecumulative_sizejava.lang.String1431688java.lang.Classandroid.icu.text.Transliterator1120227android.icu.text.TransliteratorRegistry1119600com.android.systemui.statusbar.phone.StatusBarNotificationPresenter$21086209com.android.systemui.statusbar.phone.StatusBarNotificationPresenter1085593java.util.Collections$SynchronizedMap1063376java.util.HashMap1063292该模块的实际实现位于 src/trace_processor/perfetto_sql/stdlib/android/memory/heap_graph/class_summary_tree.sql它先INCLUDE PERFETTO MODULE android.memory.heap_graph.class_tree构建类树再基于graphs.scan模块的_graph_aggregating_scan做自底向上的累计扫描把每个节点的self_count/self_size逐层累加为cumulative_count/cumulative_size。输出表android_heap_graph_class_summary_tree的核心列包括graph_sample_tsdump 时间戳upid进程 idname类名root_type若该节点是根描述 Java 根类型否则为 NULLself_count/self_size类名与到根路径均相同的对象个数 / 大小cumulative_count/cumulative_size本节点及所有后代节点的self_count/self_size之和。除class_summary_tree外同目录还提供了更丰富的分析工具链见 src/trace_processor/perfetto_sql/stdlib/android/memory/heap_graph/dominator_tree.sql支配树、class_tree.sql、object_tree.sql、bitmap.sql、excluded_refs.sql、experimental_flamegraph.sql、heap_graph_class_aggregation.sql、heap_graph_stats.sql等分别用于支配关系分析、对象级树、位图展示、排除引用、火焰图重建与统计汇总。底层实现与解析链路从实现层面看堆图数据在 trace_processor 中的导入与追踪由 src/trace_processor/importers/art_hprof/art_hprof_parser.cc 与 src/trace_processor/importers/proto/heap_graph_tracker.cc 完成后者配套的单元测试见 heap_graph_tracker_unittest.cc。与之相关的还有heap_graph_object_data内部表HPROF 特有仅 ART heap dump 填充存放解码后的字符串内容value_string与原始数组数据引用array_data_id、array_data_hash这印证了heap dump 不含对象数据、但 HPROF 路径可选择性保留字符串与数组内容的设计取舍。围绕堆图仓库还预置了若干指标 SQL见 src/trace_processor/metrics/sql/android/java_heap_class_stats.sql 与 java_heap_histogram.sql可直接复用于类级统计与堆大小直方图分析。完整的表结构与 stdlib 模块文档可结合 分析文档目录 进一步查阅。总结ART Heap Dump 是 Perfetto 针对 Java/Kotlin 堆内存问题提供的高信号量数据源它只记录引用图、不携带对象数据因而体积可控且天然适合回答谁留住了内存这类问题。在 Android 11 上通过android.java_hprof数据源捕获后既可以在 UI 中借助最短路径与支配树两种火焰图快速定位可疑对象也可以回到 trace_processor 用heap_graph_*三张表 stdlib 的class_summary_tree/dominator_tree模块做可复现、可脚本化的深度分析。【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表