【内存泄漏】内存泄漏及常见的内存泄漏检测工具介绍

内存泄漏介绍

什么是内存泄漏

内存泄漏是指程序分配了一块内存(通常是动态分配的堆内存),但在不再需要这块内存的情况下未将其释放。内存泄漏会导致程序浪费系统内存资源,持续的内存泄漏还导致系统内存的逐渐耗尽,最终导致程序或系统崩溃。

内存泄漏和常驻内存区别

常驻内存(Resident Set)是指进程在运行期间占用的内存大小,包括进程使用的代码、数据和其他资源。常驻内存是进程在运行期间一直驻留在内存中的部分,即使在进程不活动时也不会被释放。
常驻内存通常不会带来显著的负面影响。

程序与进程里的内存布局

下图是源码与 ELF(可执行可链接) 文件以及运行起来后内存布局的简易映射关系图。

程序中的初始化全局变量和局部静态变量被编译到 .data,未初始化的全局变量和局部静态变量编译后放在 .bss 段,代码主体和函数主题存放在 .text 段,ELF 文件内实际有很多段(参考《程序员的自我修养-链接,装载与库》第三章)。
当程序运行时,会将 ELF 文件加载到内存。不同的段会加载到内存布局中的不同位置,其中 heap 这部分就是程序员手动去动态申请和释放内存的部分。当程序员用 malloc 函数申请了一块内存,使用完之后却没有 free 它的时候,就会发生内存泄漏。内存泄漏得越多,进程中可以使用内存的空间就越少,时间长了就会导致系统响应慢,甚至程序崩溃。

如何“观察”内存泄漏是否发生?

在 Android 系统上通常可以用 dumpsys meminfo 命令查看进程的内存使用数据,重复 dump 后从数据的变化情况来大致判断是否有内存泄漏。

也可以借助python 或者其他一些工具将数据可视化方便查看数据变化趋势。

但这只能大致的给你展示数据变化的趋势,而非直接明白的告诉你是否发生了内存泄漏。因此我们需要更精确的工具来检测是否也有内存泄漏。

常见的内存检测工具介绍

本节我们将依次介绍 Malloc Debug, libmemunreachable, Asan, HwASan, MTE, Heapprofd, Memcheck(Valigrind)
内存泄漏检测工具(https://source.android.com/docs/core/tests/debug/native-memory?hl=zh-cn)

Malloc Debug

简介

Malloc Debug 是一种调试本机内存问题的方法。 它可以帮助检测内存损坏、内存泄漏和释放后使用问题。
Malloc Debug 通过对常规的 allocation 函数包装了一层来记录和分析内存的申请和释放。这些函数包括:malloc, free, calloc, realloc, posix_memalign, memalign, aligned_alloc, malloc_usable_size

使用方法

运行程序前的设置

adb shell setprop libc.debug.malloc.options "\"backtrace guard leak_track backtrace_dump_on_exit backtrace_dump_prefix=/sdcard/heap"\"
adb shell setprop libc.debug.malloc.program xxx(进程名)

参数介绍:

  • backtrace: 开启堆栈记录。
  • guard: 开启内存越界检测。
  • leak_track: 程序在退出时,如有内存泄漏,而不产生abort。
  • backtrace_dump_on_exit: 程序退出时dump堆栈和内存信息。
  • backtrace_dump_prefix: dump文件存放的路径和文件名的开头字符。如本处生成的文件放在/sdcard/目录下,文件名开头为heap字样,注意指定的路径要有写权限。
  • libc.debug.malloc.program: 用于设置检测的程序,不设置则检测所有的运行的程序。

执行待测试程序

  1. 离线程序
    离线程序运行完成后会在 backtrace_dump_prefix 设定的路径下存储 dump 文件
  2. 在线程序
    需要先停掉程序所在的进程,再重启该进程才会生效。
    由于在线程序一般不会主动退出(如 camerahalserver),需要使用命令来主动触发 dump。
    命令:kill -47 xxx(进程ID),注意多次触发新文件覆盖之前的文件。
    当你的程序有内存泄漏问题的话,输出如下报告:
E malloc_debug: +++ memtest leaked block of size 48 at 0x7a4a6a42e0 (leak 1 of 2)
E malloc_debug: Backtrace at time of allocation:
E malloc_debug:           #00  pc 000000000004461c  /apex/com.android.runtime/lib64/bionic/libc.so (malloc+76)
E malloc_debug:           #01  pc 00000000000c83e8  /apex/com.android.runtime/lib64/bionic/libc.so (__register_atfork+40)
E malloc_debug:           #02  pc 000000000005460c  /apex/com.android.runtime/lib64/bionic/libc.so
E malloc_debug:           #03  pc 00000000000613a0  /apex/com.android.runtime/bin/linker64
E malloc_debug:           #04  pc 0000000000061144  /apex/com.android.runtime/bin/linker64
E malloc_debug:           #05  pc 0000000000061144  /apex/com.android.runtime/bin/linker64
E malloc_debug:           #06  pc 00000000000d5f14  /apex/com.android.runtime/bin/linker64
E malloc_debug:           #07  pc 00000000000d4e0c  /apex/com.android.runtime/bin/linker64
E malloc_debug:           #08  pc 0000000000064004  /apex/com.android.runtime/bin/linker64
E malloc_debug: +++ memtest leaked block of size 20 at 0x793a6ae9a0 (leak 2 of 2)
E malloc_debug: Backtrace at time of allocation:
E malloc_debug:           #00  pc 000000000004461c  /apex/com.android.runtime/lib64/bionic/libc.so (malloc+76)
E malloc_debug:           #01  pc 00000000000100b8  /data/local/tmp/memtest/memtest
E malloc_debug:           #02  pc 00000000000546e8  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+104)
E malloc_debug: Dumping to file: /sdcard/heap.19748.exit.txt

注意报告中并不是所有的 leak 都是真正的内存泄漏,有些可能是常驻内存,开发者需要自己判断。
还需注意dump 路径要有写权限

很多时候在线运行环境下so 是无符号的程序,我们需要解析 dump 文件定位代码行号
python3 native_heapdump_viewer.py --symbols ./symboldir/ ./heap.4169.exit.txt --html > memtest4169.html

–symbols 指定的是符号库/程序的路径,子目录的路径必须要在手机上的路径一致。比如可执行程序在手机里的路径是/vendor/bin/memtest,那解释时它的带符号的程序路径上需要是 ./symboldir/vendor/bin/memtest

检测出来的并不是都是泄漏,一部分是属于常驻内存,尤其对于在线程序,我们需要将程序运行不同的次数,抓出不同的log来做对比,找出真正增长的部分。

libmemunreachable

简介

Android 的 libmemunreachable 是一个零开销的本地内存泄漏检测器。 它会在触发内存检测的时候遍历进程内存,同时将任何不可访问的块报告为泄漏。

命令行方式使用

设置属性

adb root
adb shell setprop libc.debug.malloc.program app_process
adb shell setprop wrap.[process] "\$\@“
adb shell setprop libc.debug.malloc.options backtrace=4

参数

  • backtrace_size 只收集泄漏指定 size 大小的 backtrace
  • backtrace_min_size=192 backtrace_max_size=320 收集泄漏 size 介于两者之间的backtrec

重启应用,执行 dumpsys -t 600 meminfo --unreachable [process].(自测没有 dump 出预期结果)。下面是一个带有内存问题的输出结果。

 Unreachable memory24 bytes in 2 unreachable allocationsABI: 'arm64'24 bytes unreachable at 71d37787d0first 20 bytes of contents:71d37787d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................71d37787e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................24 bytes unreachable at 71d37797d0first 20 bytes of contents:71d37797d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................71d37797e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

添加代码方式使用

官方提供4个接口来检测内存
C interface

  • bool LogUnreachableMemory(bool log_contents, size_t limit)
  • bool NoLeaks()

C++ interface

  • bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit = 100)
  • std::string GetUnreachableMemoryString(bool log_contents = false, size_t limit = 100)

核心函数是 GetUnreachableMemory() 其他三个函数内部都会调用此函数。
在使用添加代码的方式打印时,需要在编译代码时需要将 libmemunreachable.so 添加到动态依赖,libmemunreachable.so 文件可以在手机 /system/lib64/libmemunreachable.so 获取。

例子:
以下是一个包含内存泄漏的例子,在f 函数中申请了x, y 两块内存,在函数返回前x 被释放,y 赋值后没有被释放。

#include "./memunreachable.h"#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>using namespace android;void f(void);
void f(void) {printf("[memtest] function f\n");int* x = (int*)malloc(10 * sizeof(int));x[0] = 0;int* y = (int*)malloc(5 * sizeof(int));y[0] = 0;y[1] = 1;y[2] = 2;y[3] = 3;y[4] = 4;free(x);
}int main(void) {printf("[memtest] hello main\n");f();// C interfaceprintf("LogUnreachableMemory()\n");LogUnreachableMemory(true, 100);return 0;
}

adb log 输出(考虑排版省去时间戳)
log 里显示有一个 20 bytes 的内存泄漏,20 正是5 个 int 的大小,对应申请但没有释放的 y 地址的内存。

// 新建 Collection process
31232 31231 I libmemunreachable: collecting thread info for process 31231...
31232 31231 I libmemunreachable: collection thread done
// fork 进程运行 sweeping process 
31233 31233 I libmemunreachable: searching process 31231 for allocations
31233 31233 I libmemunreachable: searching done
31233 31233 I libmemunreachable: sweeping process 31231 for unreachable memory
31233 31233 I libmemunreachable: sweeping done
31233 31233 I libmemunreachable: folding related leaks
31233 31233 I libmemunreachable: folding done
// 回到 Original process 接收检测结果
31231 31231 I libmemunreachable: unreachable memory detection done
31231 31231 E libmemunreachable: 20 bytes in 1 allocation unreachable out of 1260 bytes in 7 allocations
31231 31231 E libmemunreachable:   20 bytes unreachable at 7a03454400
31231 31231 E libmemunreachable:    contents:
31231 31231 E libmemunreachable:    7a03454400: 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 ................
31231 31231 E libmemunreachable:    7a03454410: 04 00 00 00                                     ....
31231 31231 E libmemunreachable:           #00  pc 000000000003e238  /apex/com.android.runtime/lib64/bionic/libc.so (malloc+84)
31231 31231 E libmemunreachable:           #01  pc 00000000000100b8  /data/local/tmp/memtest/memtest_libmemunreachable
31231 31231 E libmemunreachable:           #02  pc 000000000004aa48  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+100)

调用 bool LogUnreachableMemory(bool log_contents, size_t limit) 时,log_contents 传 true 会打印泄露地址的内容,也就是 contents 对应的两行内容。之后可以用 address2line 解析行号。

其他内存检测工具简介

ASan

  • Asan(AddressSanitizer) 适用于检测内存越界访问、缓冲区溢出、内存泄漏等问题。它是一个在编译时插入的工具;
  • 运行时有一定的性能开销(约增加两倍),代码大小和内存均有额外开销;
  • 需要重新编译程序,编译时添加 address 相关选项;
  • 可用于 linux 和 android,但在 android 上逐步被 HwASan 取代;
  • 需要刷与 ASan 兼容的 ROM;
  • 不再受支持,即使有bug 也不会修复;

HWASan

  • HWASan 利用硬件特性,适用于检测内存错误,类似于 ASan,但能够更高效地运行在一些支持硬件特性的平台上。
  • 性能开销和 Asan 接近,但内存占用更小;
  • 需要重新编译程序,编译时添加 hwaddress 相关选项
  • 仅适用于 Android 10 及更高版本,AArch64 硬件;
  • 需要刷与 HWASan 兼容的 ROM;

MTE

  • MTE(Memory Tagging Extension) 使用硬件标签来检测内存错误,主要专注于检测内存越界访问。
  • 提供了较低的性能开销,首次具备了线上部署的可能。
  • 无需重新构建代码来检测堆错误(但需要重新构建代码来检测堆栈错误)
  • Android 系统在 Arm v9 上开始支持,仅适用于64位应用/程序;

Heapprofd

  • Heapprofd 是一个跟踪给定时间段内 Android 进程的堆分配和释放的工具。
  • 可以借助 Perfetto 抓取,开发人员可以使用该工具调查内存问题(调用栈和内存分配)。

    当开启连续 dump 后,开发者可以查看程序结束前内存占用是否合理,以检查是否有潜在内存泄漏问题。或者将待测试代码循环执行,比较每执行一次代码段后内存是否有增加,一次判断是否有内存泄漏。

Valgrind 中的Memcheck

  • Memcheck 是 Valgrind 工具套件中的一个工具,用于检测 C 和 C++ 程序中的内存错误。
  • 内存问题检测比较全面,但对性能影响比较大,耗时增加10x~20x,不适用对时间敏感的程序。
  • 在 Ubuntu 上安装:sudo apt-get install valgrind

使用 memcheck 的基本方法

  • 编译程序时加上 –g 选项,编译优化选项建议选择 -O1;
  • 使用 Valgrind 命令运行程序:valgrind --leak-check=yes myprog arg1 arg2valgrind 使用 --tools 来指定 debug 工具,而 Memcheck 是默认工具,可以省略 --tools=memcheck 选项;
  • 程序运行后输出问题报告。
$ valgrind --leak-check=yes ./memtest_origin 
==19517== Memcheck, a memory error detector
==19517== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19517== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19517== Command: ./memtest_origin
==19517== 
[memtest] hello main
[memtest] function f
==19517== 
==19517== HEAP SUMMARY:
==19517==     in use at exit: 20 bytes in 1 blocks
==19517==   total heap usage: 3 allocs, 2 frees, 1,084 bytes allocated
==19517== 
==19517== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==19517==    at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==19517==    by 0x1086FF: f() (memtest_origin.cc:9)
==19517==    by 0x108731: main (memtest_origin.cc:16)
==19517== 
==19517== LEAK SUMMARY:
==19517==    definitely lost: 20 bytes in 1 blocks
==19517==    indirectly lost: 0 bytes in 0 blocks
==19517==      possibly lost: 0 bytes in 0 blocks
==19517==    still reachable: 0 bytes in 0 blocks
==19517==         suppressed: 0 bytes in 0 blocks
==19517== 
==19517== For counts of detected and suppressed errors, rerun with: -v
==19517== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

总结

本文我们介绍了内存泄漏的概念,malloc debug 和 libmemunreachable 的使用方法,以及一些其他内存检测工具的简介,下一篇我们将介绍 malloc debug 和 libmemunreachable 的工作原理。

参考链接

  1. 【内存】Android C/C++ 内存泄漏分析 unreachable
  2. 调试本地内存使用  |  Android 开源项目  |  Android Open Source Project
  3. 调试和减少内存错误  |  Android NDK  |  Android Developers (google.cn)
  4. Malloc Debug (googlesource.com)
  5. Malloc Hooks (googlesource.com)
  6. libmemunreachable (googlesource.com)
  7. Memcheck: a memory error detector
  8. Heap profiler - Perfetto Tracing Docs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/577207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android内存管理机制概览

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、相关概念3.1 垃圾回收3.2 应用内存的分配与回…

插入排序详解(C语言)

前言 插入排序是一种简单直观的排序算法&#xff0c;在小规模数据排序或部分有序的情况下插入排序的表现十分良好&#xff0c;今天我将带大家学习插入排序的使用。let’s go ! ! ! 插入排序 插入排序的基本思想是将待排序的序列分为已排序和未排序两部分。初始时&#xff0c…

商务大厦安装电气火灾监控系统,从源头监控电气火灾

安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;介绍分析剩余电流式电气火灾监控系统的特点、组成、设计依据、监控原理和实施方案&#xff0c;并结合上海市某大厦工程设计实例探讨该系统在高层建筑中的设置要求和应用方式&#xff0c;供设计人员参考。 关键词&…

蓝桥杯嵌入式LED

1.LED原理图 2.CubeMX的LED的GPIO 3.创建LED.c和.h文件添加到bsp文件 添加bsp文件路径在c/c中 4.LED相关代码

Django之DRF框架三,序列化组件

一、序列化类的常用字段和字段参数 常用字段 字段名字段参数CharFieldmax_lengthNone, min_lengthNone, allow_blankFalse, trim_whitespaceTrueIntegerFieldmax_valueNone, min_valueNoneFloatFieldmax_valueNone, min_valueNoneBooleanFieldNullBooleanFieldFloatFieldmax_…

【隐私保护】使用Python从文本中删除个人信息:第一部分

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

【开源】基于JAVA语言的大学生相亲网站

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询会员4.2 查询相亲大会4.3 新增留言4.4 查询新闻4.5 新增新闻 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的大学生相亲网站&#xff0c;包含了会员管理模块、新闻管…

图灵日记之java奇妙历险记--输入输出方法数组

目录 输入输出输出到控制台从键盘输入使用 Scanner 读取字符串/整数/浮点数使用 Scanner 循环读取 猜数字方法方法定义方法调用的执行过程实参和形参的关系(重要)方法重载 数组数组的创建数组的初始化动态初始化静态初始化 数组的使用元素访问遍历数组 数组是引用类型null数组应…

龙芯杯个人赛串口——做一个 UART串口——RS-232

文章目录 Async transmitterAsync receiver1. RS-232 串行接口的工作原理DB-9 connectorAsynchronous communicationHow fast can we send data? 2.波特率时钟生成器Parameterized FPGA baud generator 3.RS-232 transmitter数据序列化完整代码&#xff1a; 4.RS-232 receiver…

CEC2013(python):六种算法(RFO、PSO、CSO、WOA、DBO、ABC)求解CEC2013

一、六种算法简介 1、红狐优化算法RFO 2、粒子群优化算法PSO 3、鸡群优化算法CSO 4、鲸鱼优化算法WOA 5、蜣螂优化算法DBO 6、人工蜂群算法 &#xff08;Artificial Bee Colony Algorithm, ABC&#xff09; 二、6种算法求解CEC2013 &#xff08;1&#xff09;CEC2013简…

一篇讲透:箭头函数、普通函数有什么区别

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热衷分享有趣实用的文章&#xff0c;希望大家多多支持&#xff0c;一起进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 什么是箭头函数 箭头函数和普通函数的区别 更简洁的语法 箭头函数…

Rancher小白学习之路

官网&#xff1a;http://docs.rancher.cn/docs/rancher1/rancher-service/load-balancer/_indexhttp://docs.rancher.cn/docs/rancher1/rancher-service/load-balancer/_indexRancher2.5集群搭建&K3S生产环境搭建手册 - 知乎 【rancher教程】十年运维大佬两小时带你搞定ran…

Order Delivery for WooCommerce电商商城订单一体化交付解决方案

Order Delivery for WooCommerce电商商城订单一体化交付解决方案 Order Delivery for WooCommerce电商商城订单一体化交付解决方案使客户可以在结账过程中轻松选择订单的交付日期和时间。 Order Delivery for WooCommerce电商商城订单一体化交付解决方案 让您的客户以流畅且…

分割时间方法

输入开始时间和结束时间以及时间间隔&#xff0c; 并以数组形式输出其中的时间段 const generateTimeSlots (startHour: number, endHour: number, intervalMinutes: number) > {const slots [];let currentTime new Date().setHours(startHour, 0, 0, 0);const endTime…

redis—String字符串

目录 前言 1.字符串数据类型 2.常见命令 3.典型应用场景 前言 字符串类型是Redis最基础的数据类型&#xff0c;关于字符串需要特别注意: 1)首先Redis中所有的键的类型都是字符串类型&#xff0c;而且其他几种数据结构也都是在字符串类似基础.上构建的&#xff0c;例如列表…

【2024 行人重识别最新进展】ReID3D:首个关注激光雷达行人 ReID 的工作!

【2024 行人重识别最新进展】ReID3D&#xff1a;首个关注激光雷达行人 ReID 的工作&#xff01; 摘要&#xff1a;数据集&#xff1a;方法模型&#xff1a;多任务预训练&#xff1a;ReID Network&#xff1a; 实验结果&#xff1a;结论&#xff1a; 来源&#xff1a;Arxiv 2023…

drf知识--05

两个视图基类 # APIView&#xff1a;之前一直在用---》drf提供的最顶层的父类---》以后所有视图类&#xff0c;都继承自它 # GenericAPIView&#xff1a;继承自APIView--》封装 继承APIView序列化类Response写接口 # urls.py--总路由 from django.contrib import admin from dj…

ElasticSearch 使用映射定义索引结构

动态映射 dynamic 可选值解释true默认值&#xff0c;启用动态映射&#xff0c;新增的字段会添加到映射中runtime查询时动态添加到映射中false禁用动态映射&#xff0c;忽略未知字段strict发现未知字段&#xff0c;抛出异常 显示映射 创建映射 PUT user {"mappings&qu…

Elasticsearch可视化平台Kibana [ES系列] - 第498篇

历史文章&#xff08;文章累计490&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 全…

three.js后处理(发光描边OutlinePass描边样式

效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div class"box-right"></div>&…