如何排查Android应用Native堆内存分配及疑似泄漏问题?
调试方案
步骤1:确认JIT缓存占用根因
你观察到的[anon:scudo:primary]高占用,结合Perfetto显示的分配来自/memfd:jit-cache (deleted)、触发源为String.matches,优先排查正则表达式使用问题:
- 遍历代码中所有
String.matches调用、Pattern实例创建逻辑,确认是否存在大量动态生成正则表达式、未复用Pattern实例的场景:Android 11的ART JIT会为频繁调用的正则匹配逻辑生成native代码缓存,若每次调用都创建新的Pattern实例,会导致JIT缓存无限膨胀,且这部分内存属于Scudo管理的Native堆,不会被普通GC回收 - 临时验证方案:在应用的debuggable构建中执行命令关闭JIT,观察Native堆占用是否回归正常:
adb shell setprop debug.mono.extra jit:false adb shell am force-stop <你的应用包名> # 重启应用复现操作后再查dumpsys meminfo
步骤2:完整采集Scudo堆分配信息
Malloc Debug仅dump出30MB是因为默认配置未包含Scudo的元数据缓存、未释放的页块,调整配置采集全量分配栈:
- 开启Malloc Debug的全栈采集,针对Scudo优化配置:
adb shell setprop wrap.<你的应用包名> 'LIBC_DEBUG_MALLOC_OPTIONS=backtrace=1,backtrace_max_stack_depth=32,fill_on_alloc=1 logwrapper' adb shell am force-stop <你的应用包名>
- 复现内存增长后,执行
am dumpheap -n <PID> /data/local/tmp/native_heap.hprof,将导出的文件用Android Studio的Profiler或者相关解析工具读取,就能看到所有Scudo堆的分配调用栈。
步骤3:排除Scudo页保留特性的误判
Scudo分配器默认会保留释放的内存页不立即返回给系统,用来提升后续分配的性能,部分场景下会出现Heap Alloc很小但Private Dirty很高的情况:
- 执行以下命令强制Scudo释放所有空闲页,执行后再查
dumpsys meminfo的Native Heap值,如果占用大幅下降就属于正常的分配器缓存,不属于泄漏:
adb shell kill -10 <PID>
注:信号10是Scudo预留的强制回收空闲内存的信号,仅对Android 10及以上系统生效
步骤4:验证修复效果
如果确认是Pattern未复用导致的JIT缓存膨胀,修改代码后做对比测试:
- 所有固定正则表达式提前编译为静态Pattern实例,重复使用
- 动态正则的场景限制缓存数量,避免无限创建新Pattern
- 再次复现操作流程,观察
[anon:scudo:primary]的占用是否下降到同设备其他应用的平均水平(25%总PSS左右)
内容的提问来源于stack exchange,提问作者Irfan Latif
相关产品推荐
相关产品推荐

