android native内存泄漏检测原理
16lz
2021-01-26
android native内存泄漏检测原理
- 1. 初始化
- 2. 释放和分配函数的处理
- 3.获取泄漏信息
- 4.header的作用
- 5.内存泄漏显示
本文根据Android 8.1编写,代码路径在bionic目录下。
1. 初始化
每个进程都会执行函数malloc_init_impl初始化内存分配框架。
1)初始化函数
void* malloc_impl_handle = dlopen(DEBUG_SHARED_LIB, RTLD_NOW | RTLD_LOCAL); //加载so库auto init_func = reinterpret_cast( dlsym(malloc_impl_handle, "debug_initialize")); // init_func 即debug_initialize,初始化void* finalize_sym = dlsym(malloc_impl_handle, "debug_finalize");//debug_finalize即 finalize_sym,结束void* get_leak_info_sym = dlsym(malloc_impl_handle, "debug_get_malloc_leak_info");//get_leak_info_sym 即debug_get_malloc_leak_info,获取泄漏信息void* free_leak_info_sym = dlsym(malloc_impl_handle, "debug_free_malloc_leak_info");//free_leak_info_sym 即debug_free_malloc_leak_info,释放泄漏信息
2)替换内存分配和释放函数
//调用debug_initialize函数,默认的分配函数是__libc_malloc_default_dispatch,即g_dispatch if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild, options)) { dlclose(malloc_impl_handle); return; } //将MallocDispatch表中的malloc替换成debug_malloc等,这个表即globals->malloc_dispatch if (!InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "debug")) { auto finalize_func = reinterpret_cast(finalize_sym); finalize_func(); dlclose(malloc_impl_handle); return; }
2. 释放和分配函数的处理
1)调用分配函数时
如果使用malloc(4) (leak_debug版,系统会分配4个字节的内存,然后在这个4字节内存的前面分配了一个头。所以整个malloc(4)产生的内存就像这样:
[AllocationEntry | space[4bytes]].
void* debug_malloc(size_t size) { if (DebugCallsDisabled()) { return g_dispatch->malloc(size);//默认的分配函数 } ScopedDisableDebugCalls disable; void* pointer = internal_malloc(size);//内部调用InitHeader if (g_debug->config().options & RECORD_ALLOCS) {//记录分配的空间和大小 g_debug->record->AddEntry(new MallocEntry(pointer, size)); } return pointer;}
2)调用释放函数时
void debug_free(void* pointer) { if (DebugCallsDisabled() || pointer == nullptr) { return g_dispatch->free(pointer);//默认的释放函数 } ScopedDisableDebugCalls disable; if (g_debug->config().options & RECORD_ALLOCS) { g_debug->record->AddEntry(new FreeEntry(pointer));//记录释放空间和大小 } internal_free(pointer);}
3.获取泄漏信息
// =============================================================================// Exported for use by ddms.// =============================================================================// Retrieve native heap information.//// "*info" is set to a buffer we allocate// "*overall_size" is set to the size of the "info" buffer// "*info_size" is set to the size of a single entry// "*total_memory" is set to the sum of all allocations we're tracking; does// not include heap overhead// "*backtrace_size" is set to the maximum number of entries in the back traceextern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory, size_t* backtrace_size) { if (g_debug_get_malloc_leak_info_func == nullptr) { return; } g_debug_get_malloc_leak_info_func(info, overall_size, info_size, total_memory, backtrace_size);//调用debug_get_malloc_leak_info}
void TrackData::GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory, size_t* backtrace_size) { ScopedPthreadMutexLocker scoped(&mutex_);.... *backtrace_size = debug_->config().backtrace_frames; *info_size = sizeof(size_t) * 2 + sizeof(uintptr_t) * *backtrace_size; *info = reinterpret_cast(g_dispatch->calloc(*info_size, total_backtrace_allocs_)); if (*info == nullptr) { return; } *overall_size = *info_size * total_backtrace_allocs_;//total_backtrace_allocs_这个变量在分配和释放是都有用到 std::vector list; GetList(&list);////获取Header的集合,按大小排序 uint8_t* data = *info; size_t num_allocations = 1; for (const auto& header : list) { BacktraceHeader* back_header = debug_->GetAllocBacktrace(header); if (back_header->num_frames > 0) { memcpy(data, &header->size, sizeof(size_t)); memcpy(&data[sizeof(size_t)], &num_allocations, sizeof(size_t)); memcpy(&data[2 * sizeof(size_t)], &back_header->frames[0], back_header->num_frames * sizeof(uintptr_t)); *total_memory += header->real_size(); data += *info_size; } }}
4.header的作用
1)header的结构
struct Header { uint32_t tag; void* orig_pointer; size_t size; size_t usable_size; size_t real_size() const { return size & ~(1U << 31); } void set_zygote() { size |= 1U << 31; } static size_t max_size() { return (1U << 31) - 1; }} __attribute__((packed));
struct BacktraceHeader { size_t num_frames; uintptr_t frames[0];} __attribute__((packed));
2)header的处理
//分配的时候调用static void* InitHeader(Header* header, void* orig_pointer, size_t size) { if (g_debug->config().options & TRACK_ALLOCS) { g_debug->track->Add(header, backtrace_found); } } //进而调用TrackData中的Add函数void TrackData::Add(const Header* header, bool backtrace_found) { pthread_mutex_lock(&mutex_); if (backtrace_found) { total_backtrace_allocs_++; } headers_.insert(header); pthread_mutex_unlock(&mutex_);} //释放的时候调用 static void internal_free(void* pointer) { .... if (g_debug->config().options & TRACK_ALLOCS) { bool backtrace_found = false; if (g_debug->config().options & BACKTRACE) { BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header); backtrace_found = back_header->num_frames > 0; } g_debug->track->Remove(header, backtrace_found); } .... } //进而调用TrackData中的Remove函数void TrackData::Remove(const Header* header, bool backtrace_found) { pthread_mutex_lock(&mutex_); headers_.erase(header); if (backtrace_found) { total_backtrace_allocs_--; } pthread_mutex_unlock(&mutex_);}
5.内存泄漏显示
void TrackData::DisplayLeaks() { std::vector list; GetList(&list); size_t track_count = 0; for (const auto& header : list) { error_log("+++ %s leaked block of size %zu at %p (leak %zu of %zu)", getprogname(), header->real_size(), debug_->GetPointer(header), ++track_count, list.size()); if (debug_->config().options & BACKTRACE) { BacktraceHeader* back_header = debug_->GetAllocBacktrace(header);//为了打印堆栈 // BacktraceHeader (Optional: For the allocation backtrace) if (back_header->num_frames > 0) {//有堆栈 error_log("Backtrace at time of allocation:"); backtrace_log(&back_header->frames[0], back_header->num_frames); } } g_dispatch->free(header->orig_pointer); }}
更多相关文章
- Android(安卓)- 第二章 Activity 探究
- cocos2dx实现获得设备的网络连接状态
- Android提高十七篇之多级树形菜单的实现[转]
- Android--调用内置的浏览器
- Android(安卓)平滑和立体翻页效果2
- Android调用相机拍摄照片并显示到 ImageView控件中
- Android上使用LibSVM
- Android学习笔记(三十):弹出信息-Toast和告警
- delphi xe5 android 开发实现手机打电话和发短信