首頁 > 技術 > 正文

Android Perfetto 系列 14:heapprofd 与内存 Profiling

2026-10-30  ·  閱讀:160
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
WITH target_process AS (
SELECT upid, pid, name
FROM process
WHERE name = 'com.example.app'
ORDER BY start_ts DESC
LIMIT 1
),
selected_dumps(label, dump_ts) AS (
VALUES
('baseline', 100000000000),
('after_action', 120000000000),
('after_release', 150000000000)
),
valid_dumps AS (
SELECT d.* FROM selected_dumps d
WHERE EXISTS (
SELECT 1 FROM heap_profile h
WHERE h.upid = (SELECT upid FROM target_process)
AND h.heap_name = 'libc.malloc' AND h.ts_end = d.dump_ts
)
),
alloc_by_dump AS (
SELECT
d.label,
a.callsite_id,
SUM(a.size) / 1024.0 / 1024.0 AS unreleased_mb,
SUM(a.count) AS unreleased_count
FROM heap_profile_allocation a
JOIN valid_dumps d ON a.ts <= d.dump_ts
WHERE a.upid = (SELECT upid FROM target_process)
AND a.heap_name = 'libc.malloc'
GROUP BY d.label, a.callsite_id
),
pivot AS (
SELECT
callsite_id,
SUM(CASE WHEN label = 'baseline' THEN unreleased_mb ELSE 0 END) AS baseline_unreleased_mb,
SUM(CASE WHEN label = 'after_action' THEN unreleased_mb ELSE 0 END) AS after_action_unreleased_mb,
SUM(CASE WHEN label = 'after_release' THEN unreleased_mb ELSE 0 END) AS after_release_unreleased_mb
FROM alloc_by_dump
GROUP BY callsite_id
)
SELECT
callsite_id,
baseline_unreleased_mb,
after_action_unreleased_mb,
after_release_unreleased_mb,
after_action_unreleased_mb - baseline_unreleased_mb AS action_delta_mb,
after_release_unreleased_mb - baseline_unreleased_mb AS retained_delta_mb,
after_action_unreleased_mb - after_release_unreleased_mb AS released_delta_mb,
CASE
WHEN after_release_unreleased_mb - baseline_unreleased_mb > 1 THEN 'leak_candidate'
WHEN after_action_unreleased_mb > baseline_unreleased_mb
AND after_release_unreleased_mb <= baseline_unreleased_mb THEN 'peak'
ELSE 'unknown'
END AS conclusion_type
FROM pivot
WHERE (SELECT COUNT(*) FROM valid_dumps) = 3
ORDER BY retained_delta_mb DESC
LIMIT 50;

这三个時間点必须先确认都有成功完成的方向 heap dump;占位時間不能直接用,本例只有三个 dump 均存在才返回结果,缺 dump 时调用方应输出不可用,不能让 CASE ... ELSE 0 把缺采集当成已释放。1 MiB 只是示例篩選阈值。

释放后仍保留才偏泄漏;操作中升高但释放后回落是峰值;长期保留但有容量上限、复用证据和压力回收策略,才可能写成缓存。

报告字段可以固定成这一组:

1
2
trace_name,process_name,upid,pid,scenario,dump_phase,dump_ts_ms,sampling_interval_bytes,dump_interval_ms,top_stack_frame,mapping_name,unreleased_mb,retained_delta_mb,total_malloc_mb,allocation_count,symbol_status,profileable_state,data_loss_status,evidence_grade,conclusion_type,next_action
heap-run01,com.example.app,42,1234,player_open,after_release,150000,4096,5000,PlayerBuffer::Alloc,libplayer.so,8.4,6.9,42.1,128,strong,profileable,clean,strong,leak_candidate,inspect_owner_module

采样间隔怎么理解

heapprofd 不是默认记录每一次分配。sampling_interval_bytes: 4096 的含义是:平均每分配 4096 bytes 采一个样本,再按采样概率归因到调用栈。足够大的 allocation 可能按真实大小记录,但不要把采样间隔理解成“低于它就估算,高于它就精确”的硬分界;不同 Android 版本和 heap 配置还会作用实际行为。

怎么选:

本文由News整理發布,僅供參考。

12345678
評論載入中…

友情連結申請友鏈

聯盟矩陣53