native内存泄漏分析
一.摘要
? ? 我們在分析內存泄漏時java內存泄漏我們可以集成LeakCanary來進行監控,出現問題時會打印出泄漏時的引用關系,那么我們native內存泄漏時如何分析呢?native內存泄漏我們可以通過malloc_debug工具來進行監控,詳細的介紹請參考:
https://android.googlesource.com/platform/bionic/+/master/libc/malloc_debug/README.md
?
二.如何使用
? ? 默認情況下高通的手機已經包含了malloc_debug的代碼,具體路徑在:bionic/libc/malloc_debug/,下邊介紹使用方法:
1.使用dumpsys meminfo --unreachable分析
1.1設置你要監控的進程:
adb shell setprop wrap.<APP> ?'"LIBC_DEBUG_MALLOC_OPTIONS=backtrace"'例如:
adb shell setprop wrap.com.smartisanos.security '"LIBC_DEBUG_MALLOC_OPTIONS=backtrace"'adb shell am force-stop com.smartisanos.security1.2重啟進程:
設置完屬性后需要重啟進程,我們先殺死進程adb shell am force-stop <APP>
如果之前的設置正常,進程重啟后你會發現你進程的父進程并不是zygote64,而是:
sh -c LIBC_DEBUG_MALLOC_OPTIONS=backtrace /system/bin/app_process64 /system/bin --application '--nice-name=com.smartisanos.security' com.android.internal.os.WrapperInit 35 28 'android.app.ActivityThread' '0'這就說明我們設置好了
1.3不斷復現你的問題并且抓取分配內存的調用棧:adb shell dumpsys meminfo --unreachable pid | tee app_size.txt
1.4對比你APP 不同native size產生的日志問題,尋找問題
例如:
com.smartisanos.security APP Native Heap: ? ?66696byte 時有如下棧信息:
?Unreachable memory65376 bytes in 1349 unreachable allocationsABI: 'arm64'48 bytes unreachable at 75aa29e0b0and 62832 similar unreachable bytes in 1309 allocationsreferencing 1248 unreachable bytes in 26 allocationsfirst 32 bytes of contents:75aa29e0b0: 18 67 43 4d 76 00 00 00 00 00 00 00 00 00 00 00 .gCMv...........75aa29e0c0: 00 00 00 00 00 00 00 00 b0 90 fa ad 75 00 00 00 ............u...#00 ?pc 000000000006a4f8 ?/system/lib64/libc++.so (operator new(unsigned long)+32)#01 ?pc 000000000007062c ?/system/lib64/libhwui.so#02 ?pc 0000000000068818 ?/system/lib64/libhwui.so#03 ?pc 000000000006c404 ?/system/lib64/libhwui.so#04 ?pc 0000000000075a84 ?/system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+340)#05 ?pc 00000000000114e8 ?/system/lib64/libutils.so (android::Thread::_threadLoop(void*)+280)#06 ?pc 00000000000ad7ec ?/system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140)#07 ?pc 000000000006827c ?/system/lib64/libc.so#08 ?pc 000000000001f550 ?/system/lib64/libc.so#09 ?pc 0000000000000000 ?<unknown>com.smartisanos.security APP Native Heap: ? ?66696byte 時有如下棧信息:
Unreachable memory271152 bytes in 5638 unreachable allocationsABI: 'arm64'48 bytes unreachable at 75a70be430and 261408 similar unreachable bytes in 5446 allocationsreferencing 9168 unreachable bytes in 191 allocationsfirst 32 bytes of contents:75a70be430: 18 67 43 4d 76 00 00 00 00 00 00 00 00 00 00 00 .gCMv...........75a70be440: 00 00 00 00 00 00 00 00 b0 90 fa ad 75 00 00 00 ............u...#00 ?pc 000000000006a4f8 ?/system/lib64/libc++.so (operator new(unsigned long)+32)#01 ?pc 000000000007062c ?/system/lib64/libhwui.so#02 ?pc 0000000000068818 ?/system/lib64/libhwui.so#03 ?pc 000000000006c404 ?/system/lib64/libhwui.so#04 ?pc 0000000000075a84 ?/system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+340)#05 ?pc 00000000000114e8 ?/system/lib64/libutils.so (android::Thread::_threadLoop(void*)+280)#06 ?pc 00000000000ad7ec ?/system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140)#07 ?pc 000000000006827c ?/system/lib64/libc.so#08 ?pc 000000000001f550 ?/system/lib64/libc.so#09 ?pc 0000000000000000 ?<unknown>我們發現這個棧是一樣的,但是占用的內存越來越大,那么我們直接去這個棧里排查是哪里創建了內存,解析了so得到的行號如下:
Stack Trace:RELADDR FUNCTION FILE:LINE000000000006a4f8 operator new(unsigned long)+32 external/libcxxabi/src/cxa_new_delete.cpp:46000000000007062c android::uirenderer::renderthread::RenderMonitor::addFrame(android::uirenderer::FrameInfo&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)+456 frameworks/base/libs/hwui/renderthread/RenderMonitor.cpp:1160000000000068818 android::uirenderer::renderthread::CanvasContext::draw()+616 frameworks/base/libs/hwui/renderthread/CanvasContext.cpp:489000000000006c404 android::uirenderer::renderthread::DrawFrameTask::run()+324 frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:1140000000000075a84 android::uirenderer::renderthread::RenderThread::threadLoop()+340 frameworks/base/libs/hwui/renderthread/RenderThread.cpp:37600000000000114e8 android::Thread::_threadLoop(void*)+280 system/core/libutils/include/utils/StrongPointer.h:?00000000000ad7ec android::AndroidRuntime::javaThreadShell(void*)+140 frameworks/base/core/jni/AndroidRuntime.cpp:1256000000000006827c __pthread_start(void*)+36 bionic/libc/bionic/pthread_create.cpp:226000000000001f550 __start_thread+68 bionic/libc/bionic/clone.cpp:47我們可以發現這個內存是在RenderMonitor.cpp的116行申請的,那么我們看下對應的代碼:
這里創建了一個MonitorTask對象,我們可以猜到這個對象估計是沒有釋放,那么我們找找它被釋放的地方,我們發現這個對象每次執行完才會被delete:
我們繼續找找如果task沒有被執行呢?我們看到如下代碼:
如果mMonitorTaskQueued為true,mAnimatorTaskQueued為true的時候,那么這個task就不會被delete了,到這里我們也就找到了泄漏的原因,因為這里不符合條件導致task永遠不會被delete,那么我們做如下修改:
當不符合條件的時候delete task就好了
?
2.使用native_heapdump_viewer.py分析
? ? 有時候我們用dumpsys meminfo --unreachable打印出來的調用棧并沒有什么用,即使內存泄漏了,這里顯示的內存大小還是差不多的,那么我們就得用native_heapdump_viewer來分析了,使用方法如下:
1.1復現問題多次dump native內存信息:
adb shell am dumpheap -n 6813 /data/local/tmp/heap_6813.txt ? // 6813是你進程的pid
1.2解析內存信息:
native_heapdump_viewer.py --symbols /home/pzc/sd/log/giant/417189/6-10/symbols ./heap_6813_109444.txt > heap_109444.txt // symbols一定要對應你的rom
1.3對比解析出來的內存信息:
例如我的兩次解析結果對比,com.smartisanos.security native size為:115588byte時的信息:
15241187 33.27% 33.27% 6038 7c73b6e550 /system/lib64/libc.so __start_thread bionic/libc/bionic/clone.cpp:4715241187 33.27% 100.00% 6038 7c73bb727c /system/lib64/libc.so __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:22615141747 33.05% 99.35% 4784 7c744fd7ec /system/lib64/libandroid_runtime.so android::AndroidRuntime::javaThreadShell(void*) frameworks/base/core/jni/AndroidRuntime.cpp:125615141747 33.05% 100.00% 4784 7c732e04e8 /system/lib64/libutils.so android::Thread::_threadLoop(void*) system/core/libutils/include/utils/StrongPointer.h:?15107271 32.97% 99.77% 4672 7c761e7ab8 /system/lib64/libhwui.so android::uirenderer::renderthread::RenderThread::threadLoop() frameworks/base/libs/hwui/renderthread/RenderThread.cpp:37614977578 32.69% 99.14% 3981 7c761e6ca0 /system/lib64/libhwui.so android::uirenderer::renderthread::SignalingRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.cpp:2714977578 32.69% 100.00% 3981 7c761e692c /system/lib64/libhwui.so android::uirenderer::renderthread::MethodInvokeRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.h:858978727 19.60% 59.95% 2262 7c761e551c /system/lib64/libhwui.so android::uirenderer::renderthread::Bridge_dumpProfileInfo(android::uirenderer::renderthread::dumpProfileInfoArgs*) frameworks/base/libs/hwui/renderthread/RenderProxy.cpp:4278441856 18.43% 94.02% 2061 7c76202b6c /system/lib64/libhwui.so android::uirenderer::FrameInfoVisualizer::dumpData(int) frameworks/base/libs/hwui/FrameInfoVisualizer.cpp:2498441856 18.43% 100.00% 2061 7c73bb26cc /system/lib64/libc.so fwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fwrite.c:838441856 18.43% 100.00% 2061 7c73bb2198 /system/lib64/libc.so __sfvwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c:608441856 18.43% 100.00% 2061 7c73bb46e8 /system/lib64/libc.so __swsetup bionic/libc/upstream-openbsd/lib/libc/stdio/wsetup.c:738441856 18.43% 100.00% 2061 7c73bb2a64 /system/lib64/libc.so __smakebuf bionic/libc/upstream-openbsd/lib/libc/stdio/makebuf.c:62com.smartisanos.security native size為:130088byte時的信息:
28115314 48.67% 48.67% 9142 7c73b6e550 /system/lib64/libc.so __start_thread bionic/libc/bionic/clone.cpp:4728115314 48.67% 100.00% 9142 7c73bb727c /system/lib64/libc.so __pthread_start(void*) bionic/libc/bionic/pthread_create.cpp:22628012378 48.50% 99.63% 7870 7c744fd7ec /system/lib64/libandroid_runtime.so android::AndroidRuntime::javaThreadShell(void*) frameworks/base/core/jni/AndroidRuntime.cpp:125628012378 48.50% 100.00% 7870 7c732e04e8 /system/lib64/libutils.so android::Thread::_threadLoop(void*) system/core/libutils/include/utils/StrongPointer.h:?27976768 48.43% 99.87% 7750 7c761e7ab8 /system/lib64/libhwui.so android::uirenderer::renderthread::RenderThread::threadLoop() frameworks/base/libs/hwui/renderthread/RenderThread.cpp:37627850347 48.21% 99.55% 7176 7c761e6ca0 /system/lib64/libhwui.so android::uirenderer::renderthread::SignalingRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.cpp:2727850347 48.21% 100.00% 7176 7c761e692c /system/lib64/libhwui.so android::uirenderer::renderthread::MethodInvokeRenderTask::run() frameworks/base/libs/hwui/renderthread/RenderTask.h:8516669534 28.86% 59.85% 4204 7c761e551c /system/lib64/libhwui.so android::uirenderer::renderthread::Bridge_dumpProfileInfo(android::uirenderer::renderthread::dumpProfileInfoArgs*) frameworks/base/libs/hwui/renderthread/RenderProxy.cpp:42715638528 27.07% 93.82% 3818 7c76202b6c /system/lib64/libhwui.so android::uirenderer::FrameInfoVisualizer::dumpData(int) frameworks/base/libs/hwui/FrameInfoVisualizer.cpp:24915638528 27.07% 100.00% 3818 7c73bb26cc /system/lib64/libc.so fwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fwrite.c:8315638528 27.07% 100.00% 3818 7c73bb2198 /system/lib64/libc.so __sfvwrite bionic/libc/upstream-openbsd/lib/libc/stdio/fvwrite.c:6015638528 27.07% 100.00% 3818 7c73bb46e8 /system/lib64/libc.so __swsetup bionic/libc/upstream-openbsd/lib/libc/stdio/wsetup.c:7315638528 27.07% 100.00% 3818 7c73bb2a64 /system/lib64/libc.so __smakebuf bionic/libc/upstream-openbsd/lib/libc/stdio/makebuf.c:62我們發現這個棧申請的內存越來越大,然后我們就去排查這個棧的代碼,我們發現最好malloc是從
FrameInfoVisualizer.cpp:249這里開始的,這里申請了內存但是并沒有釋放,這里也沒有fclose(file);
查了一下fprintf的資料,大概意思是:
?fprintf?:寫入指定的流(這里會創建內存)
?dprintf?:寫入指定的文件描述符(這個看上去并不會創建內存,而是直接寫到fd里)
對比了下Q上的代碼發現google也發現了這里有問題,并做了修改:
commit 5a44b4ff74fa7b27963c22249c815aef6225cb8d Author: John Reck <jreck@google.com> Date: Mon Nov 13 11:32:39 2017 -0800Fix leak of FILE* in dumpingAvoid fdopen as fclose, which frees the FILE*, will closethe FD which we don't want. Just normalize on dprintf instead,and we can add buffering if it turns out to matter at some pointTest: ran 'dumpsys gfxinfo framestats' in a loop while observing PSSChange-Id: I7808753641aa1055cfdf570c3e017017f11f1dee感興趣的同學可以再自行研究一下?fprintf?和?dprintf?的區別,這里就不繼續介紹了
?
出現問題的原因:
? ? 正常用戶不會遇到這個問題,這個只有在dumpsys gfxinfo APP的時候會走到,我們性能監控的工具剛好每隔幾秒鐘會去dumpsys一次,然后就會一直泄漏,修復方案:
https://android.googlesource.com/platform/frameworks/base/+/47f5c3a234c5c201ef640489af3ff25b5eec6652
三.總結
? ? 分析內存泄漏要是沒有掌握好調試工具,猶如大海撈針,分析異常的困難,掌握了malloc_debug的使用方法,可以讓大家分析native內存泄漏問題的效率也大大的提升~
總結
以上是生活随笔為你收集整理的native内存泄漏分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019.7.29学习整理python
- 下一篇: Dubbo核心概念