android native内存泄漏检测原理

Android Raphael使用(专治native 内存泄漏) 借用字节 开源的Raphael 快速定位 哪些so库中存在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);
  }
}
Android Native内存泄漏检测方案详解 本文介绍了四种Android Native层检测内存泄漏的方法:AddressSanitizer、LeakSanitizer、Valgrind和手动检测。这些方法有助于开发者预防和解决内存泄漏。 阅读详情

相关推荐

Android Native内存泄漏检测实战:MemoryLeakDetector从安装到分析全流程

本文提供了Android Native内存泄漏检测工具MemoryLeakDetector的完整实战指南。详细介绍了Native内存泄漏的危害与特点,对比了主流检测工具,并分步讲解了从项目集成、权限配置、监控启停到报告分析与问题定位的全流程,帮助开发者有效监控和解决隐蔽的Native层内存问题。

weixin_29268331的博客 340

Android Native内存泄漏案例

Android Native内存泄漏检测

好好学习,天天向上 1256

深度解析PLT Hook技术原理,手把手实现Native内存泄漏监控,助您打造高稳定性的监控工具,提升应用性能与用户体验

本文详细介绍了基于PLT Hook技术的Android Native内存泄漏监控系统的实现方法。从内存泄漏概念、PLT Hook原理到具体代码实现,提供了一套完整的解决方案。通过解析ELF文件结构、定位目标函数的GOT地址、修改内存权限并实现自定义内存管理函数,构建了高效的内存泄漏监控系统。本文还提供了性能优化策略、多线程环境下的监控、调试符号解析、与Java层的交互以及动态加载和卸载监控等高级特性,帮助读者解决常见问题并提高监控系统的稳定性和准确性。

全栈 1443

Android Native 内存泄漏检测全解析:从原理到工具的深度实践

Android Native内存泄漏检测技术 摘要: 本文深入分析了Android Native层内存泄漏的检测技术。Native内存泄漏由于不受JVM管理,长期积累会导致严重性能问题,约30%的OOM崩溃由此引发。检测面临三大挑战:内存分配函数拦截、堆栈信息获取和符号还原。核心解决方案包括: 使用钩子函数(__malloc_hook)或LD_PRELOAD拦截malloc/free调用 通过backtrace或libunwind库捕获调用堆栈 依赖符号表还原函数名和行号 文章详细讲解了自定义内存分配器的实

顾林海 2774

Android内存泄漏检测实战:malloc_debug从配置到问题定位全流程

本文详细解析了Android Native内存泄漏检测工具malloc_debug的配置与使用全流程,包括环境配置、问题定位技巧及性能优化策略。通过实战案例展示如何高效检测和修复内存泄漏问题,帮助开发者提升应用稳定性和性能。

weixin_29211309的博客 466

内存泄漏系列专题分析之一:内存泄漏系列专题SOP及理论知识

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。

930

深入解析 malloc_debug:Native 内存泄漏检测原理与 Android 实战指南

在 C/C++ 开发中,内存管理是核心基础,而 Native Heap 内存泄漏是常见且棘手的问题。其原理在于程序通过 malloc、calloc 等函数动态申请内存后,未能通过 free 函数正确释放,导致系统可用内存持续减少。这类问题不仅影响程序稳定性,严重时还会引发进程崩溃。从技术价值看,高效的内存泄漏检测工具能极大提升软件质量与排查效率。在 Android 平台及 Linux 环境中,malloc_debug 便是一款基于 LD_PRELOAD 机制实现的经典工具。它通过拦截标准内存分配函数,记录每

weixin_33314040的博客 293

native_heapdump_viewer.py解析app内部so

修改native_heapdump_viewer.py使得可以解析app自己的so

1204

西瓜视频开源神器MemoryLeakDetector:如何用它揪出Native内存泄漏的元凶?

本文深入探讨了西瓜视频开源的MemoryLeakDetector工具如何精准定位和解决Native内存泄漏难题。文章从Native泄漏的独特挑战出发,详细解析了该监控工具的设计哲学、实战部署步骤、核心报告分析方法以及高级差分比对策略,帮助开发者系统性地构建排查体系,有效提升应用稳定性。

food6的博客 68

Android Native 内存泄漏系统化解决方案

主要介绍了Android Native 内存泄漏系统化解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Windows游戏多开检测实战:5种技术原理与破解方法详解(附C++代码)

本文深入解析了Windows游戏多开检测的5种核心技术原理,包括进程枚举、互斥体、信号量、窗口及文件映射检测,并详细探讨了对应的破解方法。通过分析这些基于进程和内核对象的攻防机制,为开发者与逆向爱好者提供了从底层理解Windows系统进程管理的实战视角,并附有核心C++代码示例。

weixin_29315091的博客 243

Android Native内存泄漏排查实战:malloc_debug原理、配置与深度分析

在移动应用开发中,内存管理是性能优化的核心挑战,尤其是Native层内存泄漏问题,它直接影响应用稳定性和用户体验。其原理在于程序在堆上动态分配内存后未能正确释放,导致可用内存逐渐耗尽。从技术价值看,精准定位Native泄漏能从根本上提升应用健壮性。在Android平台上,针对JNI、音视频编解码等Native代码场景,系统提供了Bionic C库中的malloc_debug工具。该工具通过挂钩内存分配函数、记录分配堆栈和保护栏检测等机制,实现了对泄漏点的精准追踪。本次实战将详解如何配置backtrace与l

weixin_29192141的博客 310

[STM32]HAL STM32F103 软件I2C读写MPU6050(附DMP库移植)

本文介绍了如何在STM32F103C8T6上通过I2C协议与MPU6050传感器通信并实现DMP功能。首先,使用STM32CubeMX配置项目,设置时钟和I2C接口。然后,编写软件I2C协议的读写函数与MPU6050交互。接着,移植DMP库并进行必要修改,获取加速度计和陀螺仪数据,并将其转换为欧拉角显示在OLED上。最后,利用Keil的逻辑分析仪监测I2C时序。

m0_56841583的博客 2325

Android Native内存优化利器:PLT Hook原理与实战应用

在Android性能优化领域,Native内存问题如内存泄漏、堆内存疯涨是常见的性能瓶颈。动态链接技术是理解Native函数调用的基础,它通过过程链接表(PLT)和全局偏移表(GOT)实现函数的延迟绑定。PLT Hook正是基于此原理,通过修改GOT表中的函数地址,实现对目标函数的拦截与监控。这项技术的核心价值在于其无侵入性和全局性,能够在不修改源码的情况下,为malloc、free等关键系统函数注入监控逻辑。在工程实践中,PLT Hook是解决复杂Native内存问题的利器,广泛应用于内存泄漏检测、分配性

weixin_28278377的博客 349

通达信波段交易公式实战:如何用副图指标精准捕捉买卖点(附完整源码)

本文深入解析通达信波段交易副图指标的构建与应用,提供完整源码实现,帮助投资者精准捕捉买卖点。通过趋势强度度量、超买超卖判断及信号过滤机制,结合K线图分析,有效提升波段交易的成功率。

weixin_29306317的博客 608

基于大模型的自动驾驶智能体:Agent-Driver架构、训练与实战指南

自动驾驶系统正从传统的模块化流水线向以智能体为核心的新范式演进。其核心原理在于将车辆视为一个能够与环境持续交互的自主决策智能体,通过多模态感知、记忆管理和基于大模型的推理能力,实现对复杂交通场景的理解与应对。这一技术价值在于利用大模型的常识推理和泛化能力,有望解决传统方法在长尾场景下面临的挑战。在应用场景上,智能体驱动架构特别适合处理动态交互密集、需高层语义理解的城区驾驶、无保护转弯及突发状况应对。本文聚焦的Agent-Driver项目,正是这一方向的典型实践,它通过融合视觉、语言大模型与强化学习,构建了一

weixin_28729843的博客 529

Android Bitmap内存优化实战:从原理到监控与治理

在移动应用开发中,内存管理是保障应用流畅与稳定的核心技术之一。其核心原理在于理解系统如何为不同类型的数据结构分配和回收内存,尤其是在处理图形资源时。有效的内存优化技术能显著提升应用性能,减少卡顿与崩溃,对于用户体验至关重要。在Android平台上,Bitmap作为承载图像数据的主要对象,其内存占用机制尤为关键。通过字节码插桩等技术手段,开发者可以实现对Bitmap创建与销毁过程的白盒化监控,精准定位内存异常与泄漏问题。本文聚焦于Bitmap内存的量化分析、ASM插桩监控实践以及主动优化策略,为构建高性能移动

weixin_33811539的博客 461

固定资产报废的损失鉴定怎么做?

在社会生产的日常中,企业经常会遇到固定资产报废的损失鉴定,那么怎么去做鉴定?哪些情况下能申请固定资产报废损失鉴定呢? 今天,华碧鉴定就和大家说一说固定资产报废损失鉴定那些事儿。 符合下列条件之一的固定资产可申请报废: 1、使用年限过长,功能丧失,完全失去使用价值,或不能使用并无修复价值的; 2、产品技术落后,质量差,耗能高,效率低,已属淘汰且不适于继续使用,或技术指标已达不到使用要求的; 3、严重损坏,无法修复的或虽能修复,但累计修理费已接近或超过市场价值的; 4、主要附件损坏,无.

weixin_50362670的博客 2116

优快云开发者周刊第32期:微信游戏推荐系统大揭秘;通过真机实现页面自动化适配;美团酒旅数据治理实践

开发者周刊将甄选Top100技术社区/团队博客的每周优质内容,集中推荐给优快云开发者,内容以优质技术文章/视频、精选活动分享为主,每周五推送一期。欢迎关注订阅。 【本周Top优质内容推荐】 西瓜视频稳定性治理体系建设二:Raphael 原理及实践 作者 | 字节跳动西瓜视频客户端团队 来源 | 字节跳动技术团队博客 摘要:Android 平台上的内存问题一直是性能优化和稳定性治理的焦点和痛点,Java 堆内存因为有比较成熟的工具和方法论,加上 hprof 快照作为补充,定位和治理..

极客日报 2268

FreeRTOS任务调度实战:如何让STM32同时处理温湿度采集、OLED显示和云端通信

本文通过一个基于STM32的物联网终端项目,详细讲解了如何利用FreeRTOS实现多任务调度,同时处理温湿度采集、OLED显示和云端通信。文章深入探讨了任务规划、堆栈分配、信号量与消息队列等核心机制,并重点优化了ESP8266通信任务的非阻塞设计与稳定性,为构建响应迅速、运行可靠的嵌入式系统提供了实战指南。

weixin_29188043的博客 331

【Bug已解决】This model‘s maximum context length is X tokens. However, you requested Y tokens 解决方案

summary_prompt = "请将以下对话历史压缩成一段简洁摘要,保留关键决策和结论:\n" + format_messages(old_part)return [{"role": "system", "content": f"此前对话摘要:{summary}"}] + recent_part这种方式能在大幅降低 token 消耗的同时,尽量保留任务推进所需的关键上下文,是目前业内做长会话 Agent 最常见的处理方式之一。方案适用场景推荐指数滑动窗口裁剪短任务,对早期历史依赖不强⭐⭐⭐。

一键难忘的博客 1万+

Android内存泄漏排查篇2(Native层泄漏)

android内存泄漏排查(native篇)

凛_Lin~~的博客 1785

RK3588部署YOLO模型 包含模型量化+npu轻量化+ubuntu+RGA图像加速和多线程模型加速 包源码

本专栏为深度学习部署全栈学习体系,包含68篇由浅入深的教程,专为零基础学习者设计。内容涵盖TensorRT与RK3588平台部署技术,包含Jetson系列和RK系列硬件部署实战,采用"理论+源码+实战"三位一体的教学模式。学习路径从基础环境搭建到模型优化、容器化部署、多硬件平台适配,最终掌握工业级AI部署能力。特色包括C++基础教学、Docker容器化、INT8量化、多线程优化等核心技术,并附带YOLOv5、人脸检测、车牌识别等多个实战项目源码。适合算法工程师转部署方向、嵌入式开发者及对

FJN110的博客 3732

浅析Android Handler消息机制的实现原理

0. 背景描述 Android系统出于对简单、高效的考虑,在设计UI体系时采用了单线程模型,即不会在多个线程中对同一个UI界面执行操作。简单是指单线程模型可以不用考虑和处理在多线程环境下操作UI带来的线程安全问题,高效是指单线程模型下无需通过额外的手段处理多线程安全问题(一般通过加锁等方式解决线程安全问题,而使用锁进行同步就会存在性能开销)。同时,因为Android应用属于UI交互型应用,需要对用户的操作进行快速响应,因此在处理UI操作的线程中不能执行网络请求、本地IO等这一类耗时操作,否则会导致用户的操作

boker_han的博客 1328

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Android Binder死亡通知机制实战:手把手教你用Java和C++实现进程间通信的‘心跳检测’

本文深入解析Android Binder死亡通知机制,提供Java和C++实现跨进程通信的‘心跳检测’方案。通过实战案例,展示如何构建高可靠的进程间通信系统,涵盖线程安全、内存管理和性能优化等关键点,帮助开发者提升应用稳定性与响应速度。

weixin_30275415的博客 457

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: Android-常用的颜色(链接)
下一篇: 云计算之概念——IaaS、SaaS、PaaS、Daas
沉默的过客
博客等级 码龄13年 60粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值