通信工程网站建设,跨境电商平台app排名,打造网站品牌,做网站原创要多少钱内存泄漏介绍
什么是内存泄漏
内存泄漏是指程序分配了一块内存#xff08;通常是动态分配的堆内存#xff09;#xff0c;但在不再需要这块内存的情况下未将其释放。内存泄漏会导致程序浪费系统内存资源#xff0c;持续的内存泄漏还导致系统内存的逐渐耗尽#xff0c;最…内存泄漏介绍
什么是内存泄漏
内存泄漏是指程序分配了一块内存通常是动态分配的堆内存但在不再需要这块内存的情况下未将其释放。内存泄漏会导致程序浪费系统内存资源持续的内存泄漏还导致系统内存的逐渐耗尽最终导致程序或系统崩溃。
内存泄漏和常驻内存区别
常驻内存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?hlzh-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: 用于设置检测的程序不设置则检测所有的运行的程序。
执行待测试程序
离线程序 离线程序运行完成后会在 backtrace_dump_prefix 设定的路径下存储 dump 文件在线程序 需要先停掉程序所在的进程再重启该进程才会生效。 由于在线程序一般不会主动退出如 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 (malloc76)
E malloc_debug: #01 pc 00000000000c83e8 /apex/com.android.runtime/lib64/bionic/libc.so (__register_atfork40)
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 (malloc76)
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_init104)
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 backtrace4参数
backtrace_size 只收集泄漏指定 size 大小的 backtracebacktrace_min_size192 backtrace_max_size320 收集泄漏 size 介于两者之间的backtrec
重启应用执行 dumpsys -t 600 meminfo --unreachable [process].自测没有 dump 出预期结果。下面是一个带有内存问题的输出结果。 Unreachable memory24 bytes in 2 unreachable allocationsABI: arm6424 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.husing 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 (malloc84)
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_init100)调用 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-checkyes myprog arg1 arg2valgrind 使用 --tools 来指定 debug 工具而 Memcheck 是默认工具可以省略 --toolsmemcheck 选项程序运行后输出问题报告。
$ valgrind --leak-checkyes ./memtest_origin
19517 Memcheck, a memory error detector
19517 Copyright (C) 2002-2017, and GNU GPLd, 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 的工作原理。
参考链接
【内存】Android C/C 内存泄漏分析 unreachable调试本地内存使用 | Android 开源项目 | Android Open Source Project调试和减少内存错误 | Android NDK | Android Developers (google.cn)Malloc Debug (googlesource.com)Malloc Hooks (googlesource.com)libmemunreachable (googlesource.com)Memcheck: a memory error detectorHeap profiler - Perfetto Tracing Docs