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);  }}

更多相关文章

  1. Android(安卓)- 第二章 Activity 探究
  2. cocos2dx实现获得设备的网络连接状态
  3. Android提高十七篇之多级树形菜单的实现[转]
  4. Android--调用内置的浏览器
  5. Android(安卓)平滑和立体翻页效果2
  6. Android调用相机拍摄照片并显示到 ImageView控件中
  7. Android上使用LibSVM
  8. Android学习笔记(三十):弹出信息-Toast和告警
  9. delphi xe5 android 开发实现手机打电话和发短信

随机推荐

  1. android webview 加载本地文件
  2. Android(安卓)unZip
  3. Android个推使用
  4. Android(安卓)RecyclerView设置多选
  5. android studio导包找不到类
  6. Android(安卓)Studio如何导入android源码
  7. Android(安卓)豆瓣
  8. 干货集中营 API 文档
  9. XML的解析中的三种方法
  10. Android技术--android屏幕适配详解