Android NDK 内存泄露检测
前言
最近寫C++代碼,老是擔心代碼存在內存泄露,膽戰心驚的,Andorid中Java層代碼內存泄露可以借助leakcanary進行檢測;找了一番,找到了PC上C++上的內存泄露檢測庫LeakTracer,于是再找了下,找到了Android上的移植版。
首先建立一個項目,在根目錄下建立thirdparty目錄,進入該目錄,clone相關庫代碼
| 1 | git clone git@github.com:lizhangqu/LeakTracer.git |
在項目src/main/cpp下建立CMakeLists.txt,內容如下
| 12345678910111213141516171819202122232425262728293031 | project(Test)cmake_minimum_required (VERSION 3.6)include_directories( ${PROJECT_SOURCE_DIR}/include/ ${PROJECT_SOURCE_DIR}/../../../../thirdparty/LeakTracer/libleaktracer/include/)set(LEAKTRACER_SOURCES ${PROJECT_SOURCE_DIR}/../../../../thirdparty/LeakTracer/libleaktracer/src/AllocationHandlers.cpp#${PROJECT_SOURCE_DIR}/../../../../thirdparty/LeakTracer/libleaktracer/src/LeakTracerC.c#檢測c代碼時打開此注釋,否則不要打開 ${PROJECT_SOURCE_DIR}/../../../../thirdparty/LeakTracer/libleaktracer/src/MemoryTrace.cpp)add_library(leaktracer STATIC ${LEAKTRACER_SOURCES})set(TEST_FILES ${CMAKE_SOURCE_DIR}/native.cpp)add_library(test-jniSHARED ${TEST_FILES})target_link_libraries(test-jnileaktracerlog) |
建立src/main/cpp/include/native.h和src/main/cpp/native.cpp文件
native.h
| 1234567891011121314151617181920212223242526272829303132333435363738 | //// Created by 李樟取 on 2017/6/4.//ifndef TEST_Hdefine TEST_Hinclude "jni.h"ifndef NELEM define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))endififndef CLASSNAMEdefine CLASSNAME "io/github/lizhangqu/test/Test"endififdef ANDROIDinclude <android/log.h>define TAG "Test"define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##__VA_ARGS__)define ALOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__)define ALOGD(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##__VA_ARGS__)define ALOGW(fmt, ...) __android_log_print(ANDROID_LOG_WARN, TAG, fmt, ##__VA_ARGS__)elsedefine ALOGE printfdefine ALOGI printfdefine ALOGD printfdefine ALOGW printfendifendif //TEST_H |
native.cpp
| 1234567891011121314151617181920212223242526272829303132333435363738394041 | void test(JNIEnv *env, jobject thiz) { }static const JNINativeMethod sMethods[] = {{ const_cast<char *>("test"), const_cast<char *>("()V"), reinterpret_cast<void *>(test)},};int registerNativeMethods(JNIEnv *env, const char *className, const JNINativeMethod *methods, const int numMethods) {jclass clazz = env->FindClass(className); if (!clazz) {ALOGE("Native registration unable to find class '%s'\n", className); return JNI_FALSE;} if (env->RegisterNatives(clazz, methods, numMethods) != 0) {ALOGE("RegisterNatives failed for '%s'\n", className);env->DeleteLocalRef(clazz); return JNI_FALSE;}env->DeleteLocalRef(clazz); return JNI_TRUE;}jint JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env; if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) { return -1;}registerNativeMethods(env, CLASSNAME, sMethods, NELEM(sMethods)); return JNI_VERSION_1_6;} |
編寫test函數,簡單進行內測泄露檢測
| 1234567891011121314151617 | class MemoryTest {};void test(JNIEnv *env, jobject thiz) {leaktracer::MemoryTrace::GetInstance().startMonitoringAllThreads();MemoryTest *memoryTest = new MemoryTest;leaktracer::MemoryTrace::GetInstance().stopAllMonitoring(); std::ofstream out;out.open("/sdcard/leaks.out", std::ios_base::out); if (out.is_open()) {leaktracer::MemoryTrace::GetInstance().writeLeaks(out);} else {ALOGE("Failed to write to \"leaks.out\"\n");}} |
運行程序后調用test函數,將/sdcard/leaks.out pull到項目根目錄
| 1 | adb pull /sdcard/leaks.out |
其內容類似如下內容
| 12 | # LeakTracer report diff_utc_mono=1496511718.682943leak, time=135120.534718, stack=0x36fd6 0x35a90 0x359a4 0x32fea 0xc952d3d0, size=1, data=� |
借助thirdparty/LeakTracer/helper/leak-analyze-addr2line工具還原內測泄露堆棧。
進入項目根目錄,執行leak-analyze-addr2line
| 1 | ./thirdparty/LeakTracer/helpers/leak-analyze-addr2line ./library/build/intermediates/cmake/debug/obj/armeabi/libtest-jni.so ./leaks.out |
在mac上,會出現一個錯誤,原因是leak-analyze-addr2line中用到了addr2line工具,而mac上如果沒有此工具,就會報錯,錯誤如下:
解決方法很簡單,將ndk目錄中的arm-linux-androideabi-addr2line拷到./thirdparty/LeakTracer/helpers/下,并重命名為addr2line,然后將./thirdparty/LeakTracer/helpers/加到環境變量中,如下代碼
| 12 | cp $ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line ./thirdparty/LeakTracer/helpers/addr2lineexport PATH=$PATH:`pwd`/thirdparty/LeakTracer/helpers |
$ANDROID_NDK_HOME表示ndk的根目錄。
之后再次調用命令
| 1 | ./thirdparty/LeakTracer/helpers/leak-analyze-addr2line ./library/build/intermediates/cmake/debug/obj/armeabi/libtest-jni.so ./leaks.out |
這時候輸出如下
native.cpp:15行出現泄露,找到15行對應的代碼,即如下代碼出現泄露
| 1 | MemoryTest *memoryTest = new MemoryTest; |
加入delete代碼,再跑一次
| 12 | MemoryTest *memoryTest = new MemoryTest;delete memoryTest; |
輸出如下
發現之前的泄露不見了
最后,值得注意的是,要想使用LeakTracer需要保留so足夠多的debug信息,否則可能不能正常檢測。
項目Demo地址見NDKMemoryLeakSample
http://fucknmb.com/2017/06/05/Android-NDK-%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E6%A3%80%E6%B5%8B/總結
以上是生活随笔為你收集整理的Android NDK 内存泄露检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Part 1 — Introductio
- 下一篇: NDK 交叉编译常用变量