android native内存泄漏检测原理


本文根据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<bool (*)(const MallocDispatch*, int*, const char*)>(
   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<void (*)()>(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 trace
extern "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<uint8_t*>(g_dispatch->calloc(*info_size, total_backtrace_allocs_));
  if (*info == nullptr) {
    return;
  }
  *overall_size = *info_size * total_backtrace_allocs_;//total_backtrace_allocs_这个变量在分配和释放是都有用到

  std::vector<const Header*> 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<const Header*> 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);
  }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值