Android内存泄漏有很多种,比如说java堆内存泄漏,native堆内存泄漏,graphic 纹理泄漏等等,其中native泄漏相对比较难以定位,java泄漏可以通过hprof文件分析堆内存定位,比较容易,但是对native泄漏来说,之前的工具都不是特别好用。
之前我们说过可以使用perfetto分析Android native物理内存泄漏,喜大普奔的是,这个功能最近被集成进Android Profiler了,更方便了。
我们用和上次一样的demo说明用法。
内存泄漏demo
我们通过一个简单的例子说明一下。
新建一个简单的cpp工程,通过JNI在activity resume的时候调用了两个测试函数。
可以看到
aaaaaaaaaaaa() malloc了内存,但是没有free,造成了泄漏。
bbbbbbbbbbbb() malloc了内存,有free,没有造成泄漏。
void aaaaaaaaaaaa() {
for (int i = 0; i < 100000; i++) {
int *a = (int *) malloc(sizeof(int));
if (a != NULL) {
*a = 100;
}
}
}
void bbbbbbbbbbbb() {
for (int i = 0; i < 100000; i++) {
int *a = (int *) malloc(sizeof(int));
if (a != NULL) {
*a = 100;
free(a);
}
}
}
显然这个内存泄漏case的复现路径是不停的执行resume,pause。通过Android profiler可以看到native内存确实是不断增长的。
使用Android Profiler分析native泄漏步骤
1. 编译debug版本的app并运行
2. 点击profiler并选中要调试的进程
3. 点击进入memory页面,选中record native allocations并点击record开始录制内存信息。
4. 重复复现路径,
这段时间内会记录申请和释放的内存信息,后面通过对比就可以发现哪些函数申请了内存没有释放了。
4. 查看内存信息
可以看到函数申请的内存以及函数调用栈都显示出来了。
Total Remaining Size:表示这段时间内函数申请了还没有释放的内存
Alloction Size:表示这段时间内函数申请过的所有内存(包括已经释放的和还没有释放的)
一个函数,如果没有泄漏,那么Alloction Size 会远大于Total Remaining Size
一个函数,如果泄漏了,那么Alloction Size会等于Total Remaining Size
通过对比这2个值在所有函数申请的内存的占比,就可以很容易的找到泄漏的函数。
图中这个例子,aaaaaaaa()函数的Alloction Size占比很小,但Total Remaining Size占比却出奇的大,说明aaaaaaaa()泄漏了。
泄漏的调用栈也打出来了,就很容易解决了。