1.环境
Android16,设备是userdebug
2.使用下面命令检查是否有内存泄漏
adb shell dumpsys meminfo --unreachable 26718,其中26718是应用的进程号,输出如下,Unreachable memory是native未回收的内存
Applications Memory Usage (in Kilobytes):
Uptime: 5082822 Realtime: 5082822
** MEMINFO in pid 10085 [com.crab.test.kotlin.mediandktest] **
Pss Private Private Swap Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 12700 12700 0 0 12700 17772 11340 2492
Dalvik Heap 9712 9712 0 0 9712 18880 9440 9440
Dalvik Other 2968 2820 0 0 3116
Stack 680 680 0 0 680
Ashmem 625 0 0 0 1892
Other dev 25 0 24 0 1104
.so mmap 19525 7316 3012 0 100316
.jar mmap 6018 0 368 0 66176
.apk mmap 12186 28 10564 0 16260
.ttf mmap 312 0 132 0 1264
.dex mmap 197 8 0 0 1580
.oat mmap 233 0 0 0 10708
.art mmap 32913 32872 8 0 33188
Other mmap 50662 2432 46804 0 55932
Unknown 2916 2852 64 0 2916
TOTAL 151672 71420 60976 0 317544 36652 20780 11932
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 42592 42900
Native Heap: 12700 12700
Code: 21452 196624
Stack: 680 680
Graphics: 0 0
Private Other: 54972
System: 19276
Unknown: 64640
TOTAL PSS: 151672 TOTAL RSS: 317544 TOTAL SWAP (KB): 0
Objects
Views: 17 ViewRootImpl: 1
AppContexts: 5 Activities: 1
Assets: 3 AssetManagers: 0
Local Binders: 15 Proxy Binders: 65
Parcel memory: 6 Parcel count: 25
Death Recipients: 0 WebViews: 0
Native Allocations
Count Total(kB)
------ ------
Other (malloced): 511 47
Other (nonmalloced): 83 62
Bitmap (malloced): 4 970
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
Unreachable memory
1797 bytes in 36 unreachable allocations
ABI: 'arm64'
56 bytes unreachable at 796666c290
referencing 1677 unreachable bytes in 34 allocations
first 32 bytes of contents:
796666c290: d0 22 64 26 7b 00 00 b4 38 a1 60 96 7a 00 00 b4 ."d&{...8.`.z...
796666c2a0: 08 97 9b e1 7b 00 00 00 00 00 00 00 00 00 00 00 ....{...........
64 bytes unreachable at 7966612b50
first 20 bytes of contents:
7966612b50: 55 73 65 72 2d 41 67 65 6e 74 3a 20 73 74 61 67 User-Agent: stag
7966612b60: 65 66 72 69 67 68 74 2f 31 2e 32 20 28 4c 69 6e efright/1.2 (Lin
3.开启Malloc Debug(应用级别)
根据文档路径(源码根目录/bionic/libc/memory/malloc_debug/README.md),"For app developers" 章节,使用 wrap.<包名> 属性开启:
# 如果 Android 12+ 且遇到问题,先执行这个修复命令(可选)
adb shell setprop dalvik.vm.force-java-zygote-fork-loop true
# 开启 malloc debug,记录 backtrace
adb shell setprop wrap.com.crab.test.kotlin.mediandktest '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper"'
com.crab.test.kotlin.mediandktest是应用的包名
验证属性是否设置成功:
adb shell getprop | grep wrap
# 应该看到: wrap.com.crab.test.kotlin.mediandktest: [LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper]
4.重复执行第二步可以看到下面输出
Applications Memory Usage (in Kilobytes):
Uptime: 11497639 Realtime: 11497639
** MEMINFO in pid 26718 [com.crab.test.kotlin.mediandktest] **
Pss Private Private Swap Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 20016 20016 0 0 20016 25056 18276 2852
Dalvik Heap 9572 9572 0 0 9572 18864 9432 9432
Dalvik Other 2988 2844 0 0 3132
Stack 884 884 0 0 884
Ashmem 625 0 0 0 1892
Other dev 656 0 24 0 2764
.so mmap 27268 7436 9656 0 111332
.jar mmap 6362 0 684 0 66596
.apk mmap 12231 28 10592 0 16324
.ttf mmap 312 0 132 0 1264
.dex mmap 205 8 16 0 1580
.oat mmap 272 0 16 0 10604
.art mmap 32913 32872 8 0 33188
Other mmap 50728 2496 46800 0 56068
Unknown 2932 2868 64 0 2932
TOTAL 167964 79024 67992 0 338148 43920 27708 12284
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 42452 42760
Native Heap: 20016 20016
Code: 28596 208016
Stack: 884 884
Graphics: 0 0
Private Other: 55068
System: 20948
Unknown: 66472
TOTAL PSS: 167964 TOTAL RSS: 338148 TOTAL SWAP (KB): 0
Objects
Views: 17 ViewRootImpl: 1
AppContexts: 5 Activities: 1
Assets: 3 AssetManagers: 0
Local Binders: 15 Proxy Binders: 65
Parcel memory: 6 Parcel count: 25
Death Recipients: 0 WebViews: 0
Native Allocations
Count Total(kB)
------ ------
Other (malloced): 511 47
Other (nonmalloced): 83 62
Bitmap (malloced): 4 970
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
Unreachable memory
1771 bytes in 33 unreachable allocations
ABI: 'arm64'
56 bytes unreachable at 752815af80
referencing 1651 unreachable bytes in 31 allocations
first 32 bytes of contents:
752815af80: f0 b4 0b 78 75 00 00 b4 18 62 00 a8 75 00 00 b4 ...xu....b..u...
752815af90: 08 37 56 9a 77 00 00 00 00 00 00 00 00 00 00 00 .7V.w...........
#00 pc 00000000000596ec /apex/com.android.runtime/lib64/bionic/libc.so (malloc+80)
#01 pc 000000000010289c /system/lib64/libc++.so (operator new(unsigned long)+28)
#02 pc 000000000007a158 /system/lib64/libmedia.so
#03 pc 000000000002036c /system/lib64/libmediandk.so
#04 pc 000000000001c724 /system/lib64/libstagefright_foundation.so (android::AHandler::deliverMessage(android::sp<android::AMessage> const&)+184)
#05 pc 000000000002373c /system/lib64/libstagefright_foundation.so (android::AMessage::deliver()+172)
#06 pc 000000000001dc24 /system/lib64/libstagefright_foundation.so (android::ALooper::loop()+536)
#07 pc 0000000000017e54 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+536)
#08 pc 0000000000137b68 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+144)
#09 pc 0000000000019eec /system/lib64/libutils.so
#10 pc 0000000000086580 /apex/com.android.runtime/lib64/bionic/libc.so
#11 pc 0000000000078d64 /apex/com.android.runtime/lib64/bionic/libc.so
64 bytes unreachable at 752801f580
first 20 bytes of contents:
752801f580: 55 73 65 72 2d 41 67 65 6e 74 3a 20 73 74 61 67 User-Agent: stag
752801f590: 65 66 72 69 67 68 74 2f 31 2e 32 20 28 4c 69 6e efright/1.2 (Lin
#00 pc 00000000000599bc /apex/com.android.runtime/lib64/bionic/libc.so (realloc+160)
#01 pc 0000000000028ca4 /system/lib64/libstagefright_foundation.so (android::AString::append(char const*, unsigned long)+120)
#02 pc 000000000002a2a0 /system/lib64/libstagefright_foundation.so
#03 pc 000000000007ca58 /system/lib64/libmediaplayerservice.so
#04 pc 0000000000059414 /apex/com.android.runtime/bin/linker64
#05 pc 00000000000593c0 /apex/com.android.runtime/bin/linker64
#06 pc 00000000000585fc /apex/com.android.runtime/bin/linker64
#07 pc 0000000000057fc4 /apex/com.android.runtime/bin/linker64
#08 pc 0000000000004024 /apex/com.android.runtime/lib64/bionic/libdl.so (dlopen+16)
#09 pc 00000000000fc890 /system/lib64/libaudioclient.so
#10 pc 000000000005946c /system/lib64/libaudioclient.so
#11 pc 0000000000059414 /apex/com.android.runtime/bin/linker64
#12 pc 00000000000593c0 /apex/com.android.runtime/bin/linker64
#13 pc 00000000000593c0 /apex/com.android.runtime/bin/linker64
#14 pc 00000000000593c0 /apex/com.android.runtime/bin/linker64
#15 pc 00000000000593c0 /apex/com.android.runtime/bin/linker64
5.从源码编译的out目录找到这些so库的符号链接,去找到对应的文件行号
比如Android16源码:
android/prebuilts/clang/host/linux-x86/clang-r574158/bin$ ./llvm-addr2line -f -C -e libstagefright_foundation.so 000000000001dc24
android::ALooper::loop()
frameworks/av/media/module/foundation/ALooper.cpp:280
android/prebuilts/clang/host/linux-x86/clang-r574158/bin$ ./llvm-addr2line -f -C -e libstagefright_foundation.so 000000000002373c
android::AMessage::deliver()
frameworks/av/media/module/foundation/AMessage.cpp:419
android/prebuilts/clang/host/linux-x86/clang-r574158/bin$ ./llvm-addr2line -f -C -e libstagefright_foundation.so 000000000001c724
android::AHandler::deliverMessage(android::sp<android::AMessage> const&)
frameworks/av/media/module/foundation/AHandler.cpp:28
android/prebuilts/clang/host/linux-x86/clang-r574158/bin$ ./llvm-addr2line -f -C -e lib
libmediandk.so libmedia.so libstagefright_foundation.so
android/prebuilts/clang/host/linux-x86/clang-r574158/bin$ ./llvm-addr2line -f -C -e lib
libmediandk.so libmedia.so libstagefright_foundation.so
android/prebuilts/clang/host/linux-x86/clang-r574158/bin$ ./llvm-addr2line -f -C -e libmediandk.so 000000000002036c
CodecHandler::onMessageReceived(android::sp<android::AMessage> const&)
frameworks/av/media/ndk/NdkMediaCodec.cpp:258
可以看到NdkMediaCodec.cpp:258泄漏了,我的代码如下:
case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
{
sp<AMessage> format;
if (!msg->findMessage("format", &format)) {
ALOGD("CB_OUTPUT_FORMAT_CHANGED: format is expected.");
break;
}
AMediaFormat *aMediaFormat = AMediaFormat_fromMsg(&format);
Mutex::Autolock _l(mCodec->mAsyncCallbackLock);
if (mCodec->mAsyncCallback.onAsyncFormatChanged != NULL) {
mCodec->mAsyncCallback.onAsyncFormatChanged(
mCodec,
mCodec->mAsyncCallbackUserData,
aMediaFormat);
}
break;
}
这里new了一个指针,异步回调给了应用层,应用层拿到这个指针没有释放,由于系统层也没有释放,所以就导致了内存泄漏。修改方法也很简单,应用层拿到这个指针的时候,在不需要使用时主动调用一次AMediaFormat_delete来释放指针就可以了。