转载请注明出处:https://www.cnblogs.com/zzcperf/articles/11615655.html
上一篇文章列举了不同版本Android OS内存泄漏的检测操作(传送门),这一篇说一下Android Native内存泄漏检测的原理。
之前所说的内存泄漏检测,主要借助了Android 原生的libc_malloc_debug.so,这一种检测方法分为以下三步:
记录内存分配的调用栈 -> 输出当前进程尚未释放的内存对应的申请调用栈 -> 找出内存泄漏的调用栈。
其实是围绕记录内存分配的调用栈这个主题。
- 记录内存分配的调用栈
为了输出尚未释放内存的调用栈,内存分配时需要记录当时的调用栈,释放内存时,也要删除相应内存申请时调用栈的信息。
内存泄漏的代码往往重复执行而没有释放,所以才导致严重的内存占用问题。
这些内存申请都有相同的调用栈,所以libc_malloc_debug.so以调用栈为Key,使用哈希表保存尚未释放的内存申请记录。
每次申请内存时,会额外申请分配一个头部指针,并在哈希表中记录本次的内存申请,最后将头部指针指向哈希表中的记录。
对于之前已经记录过内存分配调用栈,就只需要对分配次数自增即可。
-> PointerData::AddBacktrace(size_t num_frames)
每次释放内存时,就根据头部指针,消去哈希表中对应的记录。
-> PointerData::RemoveBacktrace(size_t hash_index)
提取哈希表的记录,就是进程当前尚未释放的内存记录
-> PointerData::GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory, size_t* backtrace_size)
- 输出当前进程尚未释放的内存对应的申请调用栈
Android N OS之后,Google有native_heapdump_viewer.py脚本,已经不需要我们自己写代码来解析获得的哈希表内容了。
这里补充略微尴尬的一点,Android N在返回结果的时候,将保存分配次数的变量,错误地赋值为调用栈栈帧数,导致脚本不得不作出规避和适配。
有兴致围观谷歌bug的话,可以对比Android N 和 O返回信息的函数:
void TrackData::GetInfo(DebugData& debug, uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory, size_t* backtrace_size)
- 找出内存泄漏的调用栈
native_heapdump_viewer.py生成的HTML文件,是树状结构。树的每一个节点代表一个栈帧,叶子节点就是申请内存的地方。
通过对比重新20次和40次的HTML文件,找上升最快的调用栈,就基本上是内存泄漏的地方。