Android Native内存泄漏管理(1):基本概念/原理

Native内存基础

Native内存的基本概念

  • 物理内存(PSS)硬件概念
    • RAM硬件
  • 虚拟内存(VSS)操作系统概念,为了解决物理内存不足的问题
    • 内存分页
    • 缺页中断
    • 页面置换
  • 安卓应用内存使用(均在用户空间内 user space:相对于kernel space的概念
    • Java使用内存
      • Java heap size * 2
      • Android oat/art文件
    • Native可用内存
      • 除Java之外的用户空间
  • App & Device运行时 用户空间虚拟内存上限(Native OOM的本质:用户空间虚拟内存耗尽)
    • 32位App & 32位Device:最大3G左右
    • 32位App & 64位Device:最大4G左右
    • 64位App & 64位Device:最大512G左右(是不是超大?所以64位app几乎不会有native OOM问题,支持64位app也是治理OOM途径之一)

虚拟内存基本原理

Native内存的分配方式

无论是*alloc还是mmap都不能直接分配物理内存,只能分配虚拟内存,但是*alloc系函数分配小块内存可能直接映射到物理内存。Thread、Webview、Flutter、显存内存分配基本都是走mmap虚拟内存

PSS

  • 不能直接被分配 只能被映射
  • malloc/calloc/realloc/memalign
  • free
  • posix_memalign
  • aligned_alloc
  • malloc_usable_size
  • pvalloc/valloc

VSS

  • 当*alloc函数分配内存 >=128k 时,底层调用mmap
  • mmap/mmap64
  • munmap
  • mremap

Native内存不足的表现

  • Java Crash(pthread_create OOM)
  • Native Crash
  • 黑屏(底层做了异常处理,可能以黑屏方式体现)

栈回溯

  • 由调用栈栈顶向栈底推到调用链的过程
  • 通常用在打印crash堆栈(例如Java层的printStackTrace()

Native内存泄漏工具

目的:找出用户虚拟内存空间被哪些业务逻辑耗尽(不光聚焦虚拟内存,还需聚焦物理内存,提高虚拟内存使用率,即找出虚拟内存中申请但未在物理内存中分配的内存,另外随着64位app的普及,在虚拟内存耗尽之前,物理内存会先耗尽并产生ANR/重启等后果)

Native内存泄漏原理

Why:与Java层不同,Java层相当于在Native层上又做了一次抽象,可以比较容易理清内存块的属性和依赖关系,所以Native内存不能像Java那样进行静态分析,只能渗透到内存分配的过程监控内存分配/释放过程
How:类似于筛子模型,过滤出分配了但没释放的内存

  • Native内存泄漏监控原理
    • 通过代理拦截内存分配的地址和大小
    • 通过回溯调用栈获取内存分配的调用链(相比Java是通过依赖关系获取引用链,然后通过引用链获得调用链)
    • 通过缓存crud过滤出未释放的内存
  • Native内存泄漏监控组成
    • 代理
    • 栈回溯
    • 缓存管理

Native内存监控原理

已有工具/解决方案

  • Malloc Debug:AOSP原生支持,难堪大用
    • 稳定性问题:存在栈回溯crash
    • 性能问题:性能损失十倍以上
  • LeakTracer:依赖LD_PRELOAD机制和系统栈回溯实现
  • MTrace:仅支持malloc/realloc等 不支持new/new[]等
  • MemWatch
  • Valgrind-memcheck
  • TCMalloc
  • LeakSanitizer
  • 高德系统化解决方案

代理方案/栈回溯方案/缓存管理方案对比

代理方案

模式 hook原理 优点 缺点
Malloc hook AOSP原生代理实现 没有性能/稳定性问题 接口不暴露,不支持mmap
LD_PRELOAD so加载先后顺序 没有性能/稳定性问题 Android上难以实现
PLT/GOT hook 跳转表 单点hook 成熟可靠 hook效率低
Inline hook 目标代码 全局hook 效率高 兼容性问题多 风险大

栈回溯方案

  • libunwind_llvm:LLVM内置unwind库,libunwind llvm编年史
  • libunwind_nongnu:第三方unwind实现
  • libgcc_s:GCC内置unwind库
  • libbacktrace:AOSP内置
  • libunwindstack:Android9.0新方案
  • libudf:MTK实现
Unwind 性能 兼容性 支持
libudf 性能最好 兼容性好 无更新
libunwind_llvm 性能较好 兼容性差 会被取代
libunwind_nongnu 性能最差 兼容性好 官方持续更新

缓存管理方案

  • Malloc Debug:全局锁,hash散列,栈聚合,动态申请缓存空间
  • LeakTracer:全局锁,大缓存,hash散列,缓存满之后动态申请缓存空间