内存工具-无标签匿名页溯源
来源:飞书文档 | 有问题可联系 @钟万勇 解决
⚠️ 图片说明:本篇 5 张图片因飞书 MCP 下载返回 403(无权限),均未能上传图床。已用飞书返回的图片描述文字替代,原始链接见每张图下方的注释,需手动补图。
解决的问题说明
业务在内存分析过程会遇到一些无标签的匿名页无法进一步拆解,会有几种具体表现为:
- 一些内存统计脚本表现为”匿名页其他”比较大
- showmap中汇总的anon很大
- 进一步表现为showmap -v中某个anon的条目占比比较大
- 再进一步可追溯到smaps中某个地址区间占用的内存大,但没有具体的标签,业务不知道这块内存的作用
➜ CatchMemDataShell git:(v1) ✗ adb shell showmap 18393 | grep "\[anon\]" -iE | sort -k 2 -r
4090436 11836 11836 0 0 0 11836 0 0 10240 0 0 0 0 0 1150 [anon]➜ CatchMemDataShell git:(v1) ✗ adb shell showmap 18393 -v | grep "\[anon\]" -iE | sort -k 2 -r | head -10
11472 11472 11472 0 0 0 11472 0 0 10240 0 0 0 0 0 rw- [anon]
320 320 320 0 0 0 320 0 0 0 0 0 0 0 0 r-- [anon]
256 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
128 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
36 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
4 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
4 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
4 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
4 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]
4 4 4 0 0 0 4 0 0 0 0 0 0 0 0 rw- [anon]......
acfc8000-aefc8000 r-xs 02000000 00:01 2922 [anon_shmem:dalvik-jit-code-cache]
Size: 32768 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
......
THPeligible: 0
VmFlags: rd ex sh mr mw me ms
aefc8000-afafc000 rw-p 00000000 00:00 0
Size: 11472 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 11472 kB
Pss: 11472 kB
Pss_Dirty: 11472 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 11472 kB
Referenced: 11472 kB
Anonymous: 11472 kB
KSM: 0 kB
LazyFree: 0 kB
AnonHugePages: 10240 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 1
VmFlags: rd wr mr mw me ac
ebad6000-ebad7000 ---p 00000000 00:00 0 [anon:dalvik-Sentinel fault page]
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 0 kB
Pss: 0 kB
......此页面介绍的工具主要帮助解决此类问题,具体效果为:会将所有smaps中没有标签的区间对应申请的调用栈打印出来,来辅助业务定位这块无标签内存区间的来源和作用,进一步做内存的优化。
📷 图片描述:内存工具中无标签匿名页溯源的示例结果。红色框内是内存地址区间 “e4d54000 - e5c58000”,其 Size 为 11728 字节,Res、Swap、SwapSize 均为 11728 字节,0x0000000000000000 处的调用栈信息被突出显示。
采集工具使用指导
完整使用指导详见:典型场景工具使用指导
工具获取
工具下载地址(采集工具 catch_mem): 已转存自有文件床,见文末「附录:工具下载」与 附件下载索引.md(网盘原链 kpan.mioffice.cn 密码 869f 作备份)。
下载最新的 catch_mem 工具。
采集命令说明
🍰 注意:采集命令中
-e参数指定 mmap。 冷启动场景的命令格式://linux
./start appdump --package_name <进程名> -b <event buf> -B <stack buf> -a <mem_use buf> -e <采集类型> -c --scenario <场景名> -s <设备序列号> -d <device name>//windows
.\start.exe appdump --package_name <进程名> -b <event buf> -B <stack buf> -a <mem_use buf> -e <采集类型> -c --scenario <场景名> -s <设备序列号> -d <device name>
例如:在 O2S 设备上采集浏览器场景的 mmap 内存日志的命令:
.\start.exe appdump -b 256 -B 256 -a 512 -c -e mmap -s 078AC89243448540 --scenario browser --package_name com.android.browser -d o2s
采集日志内容
采集完成后的内存日志目录结构如下:
📷 图片描述:内存工具采集完成后的内存日志目录结构。目录名为 “browser_traces_o2s”,包含多个文件夹,如 gfs_data、hprof、log_data 等,其中 “mem_use_data” 和 “memuse_filename_data” 文件夹的修改日期为 2026/2/10 14:29,“showmap_data” 文件夹为 14:30,“simpleperf_data” 为 14:24,“stack1_data” 和 “stack2_data” 为 14:30,“symbol_data” 为 14:32,“traces” 为 14:31。
导出测试场景符号库
符号导出工具和日志采集工具在同目录,工具名称为 collect_libs.exe。
//获取应用的进程名
adb shell ps -ef | grep 进程名
//进程号为要采集进程的进程号
//linux
./collect_libs --pid <进程号> --pull <symbol输出目录>
//windows
.\collect_libs.exe --pid <进程号> --pull <symbol输出目录>将导出的符号库文件放到内存日志采集目录中的 symbol_data 目录(目录不存在时手动创建,要确保目录名称必须为 symbol_data)中。
📷 图片描述:内存日志采集完成后目录结构中的 symbol_data 文件夹内容。文件夹内包含多个文件夹,如 apex、appdump、data、product、system、system_ext、vendor 等,均为 2026 年 2 月 10 日 14:32 创建。
解析工具使用指导
工具下载地址(解析工具 smaps_memory_data_process): 已转存自有文件床 → smaps_memory_data_process (303M)(更多见 附件下载索引.md)。
📷 图片描述:内存工具目录结构中的 smap_process 文件夹内容。
🌰 注意:工具为网盘 smap_process 目录中的
smaps_memory_data_process可执行文件,该工具只能放置在 Linux 环境上使用。
目录结构要求
确保如下所示的必要日志文件目录已经存在:
设备目录/
├── mem_use_data/ # 存放后缀为 .use.data 的二进制文件
├── stack1_data/ # 存放后缀为 .stack1.data 的二进制文件
├── stack2_data/ # 存放后缀为 .stack2.data 的二进制文件
├── memuse_filename_data/ # 存放后缀为 .filename.data 的二进制文件
├── showmap_data/ # 存放后缀为 .end.smap 的 Smaps 文件
└── symbol_data/ # 存放符号库文件解析工具的命令行参数说明
| 参数 | 说明 | 必填 |
|---|---|---|
--input_nums | 输入目录的个数 | 是 |
--input_dirs | 输入目录列表(多个目录用空格分隔) | 是 |
--output_dir | 输出结果目录 | 是 |
使用示例
处理单个设备的内存日志
smaps_memory_data_process \
--input_nums 1 \
--input_dirs /path/to/device1 \
--output_dir /path/to/output例如解析上述采集到的 browser_traces_o2s 目录中的日志时,使用如下命令:
./smaps_memory_data_process --input_nums 1 --input_dirs ~/browser_traces_o2s --output_dir ~/output执行完成后的输出文件都放置在 ~/output 目录中。
处理多个设备的内存日志
smaps_memory_data_process \
--input_nums 3 \
--input_dirs /path/to/device1 /path/to/device2 /path/to/device3 \
--output_dir /path/to/output输出文件
解析工具运行完成后会在指定的输出目录中生成以下文件:
| 文件类型 | 文件名格式 | 说明 |
|---|---|---|
| 合并文件 | {设备名}_{基础名}_merged.txt | 合并后的内存日志 |
| 符号化文件 | {设备名}_{基础名}_merged.symbolized.txt | 符号化后的内存日志 |
| CSV 报告 | {设备名}_{文件名}.csv | Smaps 解析结果 |
最终需要关注的结果文件为 CSV 报告文件,文件中会详细的展示每个匿名内存的映射区间,以及该映射发生时的调用栈轨迹信息(不关注的列信息可以隐藏)。
📷 图片描述:内存解析工具生成的 CSV 报告文件内容。报告中记录了多个匿名内存的映射区间(如 0x645000 - 0x6c58000 等),以及对应的调用栈轨迹信息。每行数据包含设备名、基础名、内存地址、大小、起始地址、结束地址、Swap、SwapSize、Res、Pos、SwapPos 等字段。
常见错误处理
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
子目录不存在: mem_use_data | 缺少必要的子目录 | 确保包含所有必需的子目录 |
缺少文件: ... | 缺少必需的数据文件 | 检查各子目录中是否存在对应后缀的文件 |
输入目录数量不匹配 | input_nums 与实际输入目录数量不一致 | 确保 input_nums 值与 input_dirs 数量一致 |
符号目录不存在: ... | symbol_data 目录不存在 | 在输入目录中创建 symbol_data 目录并放入符号文件 |
注意事项
- 文件命名 - 输出文件会使用输入目录名称作为前缀,请确保输入目录名具有可识别性
- 目录权限 - 确保对输入目录和输出目录都有读写权限
- 符号文件 -
symbol_data目录中需要包含完整的符号库文件 - 处理时间 - 大文件处理可能需要较长时间,请耐心等待
附录:工具下载(已转存自有文件床)
网盘
kpan.mioffice.cn(密码869f)里的工具已全部镜像到自有文件床,可直接下载。完整清单(66 文件 / 全版本 / 全平台)见 附件下载索引.md。本篇核心工具直链:
| 工具 | 大小 | 下载 |
|---|---|---|
smaps_memory_data_process(本文档解析工具) | 303M | 下载 |
start.exe(采集,最新版 catch_1_0_4 / Windows) | 79M | 下载 |
collect_libs.exe(symbol,catch_1_0_4 / Windows) | 9M | 下载 |
start(采集,catch_1_0_4 / Linux ubuntu24) | 51M | 下载 |
collect_libs(symbol,catch_1_0_4 / Linux ubuntu24) | 7M | 下载 |
📦 全部 66 文件(catch_1_0_0 ~ 1_0_4 全版本、Windows/Linux、解析工具等):见 附件下载索引.md 🔗 网盘原链(备份,含未镜像的依赖库/旧版本完整包):https://kpan.mioffice.cn/webfolder/ext/H507l6zdDJX$uVm31GQvyw@@ | 密码
869f




