【Android Linux内存及性能优化】(七) 程序内存泄漏检查工具

本文接着
【Android Linux内存及性能优化】(一) 进程内存的优化 - 堆段
【Android Linux内存及性能优化】(二) 进程内存的优化 - 栈段 - 环境变量 - ELF
【Android Linux内存及性能优化】(三) 进程内存的优化 - ELF执行文件的 数据段-代码段
【Android Linux内存及性能优化】(四) 进程内存的优化 - 动态库- 静态库
【Android Linux内存及性能优化】(五) 进程内存的优化 - 线程
【Android Linux内存及性能优化】(六) 系统内存的优化

一、内存篇

1.1 系统当前可用内存

1.2 进程的内存使用

1.3 进程内存优化

1.4 系统内存优化

1.5 内存泄漏

解决内存泄漏,一个最好的办法就是,不要让你的进程成为一个守护进程,完成工作后立刻退出,Linux 会自动回收该进程所占有的内存,这样你写的程序即使存在一些内存泄漏,也无关大局。

1.5.1 mtrace() 和 muntrace()

首先介绍glibc 针对内存泄漏给出的解决方法, 在glibc 库中实现了一个钩子函数mtrace,mtrace的使用方法如下:

  1. 加入头文件<mcheck.h>
  2. 在需要内存泄漏检查的代码的开始调用 void mtrace(),在需要内存泄漏检查代码结尾调用 void muntrace()
    一般情况下不要调用 muntrace,而让程序自然结束,因为有些内存释放代码要到 muntrace 之后运行。
  3. 用 debug 模式编译检查代码(-g 或 -ggdb)
  4. 在运行程序之前,先设置环境变量MALLOC_TRACE 为一文件名,这一文件将有内存分配信息。
  5. 运行程序,内存分配的log 将输出到 MALLOC_TRACE 所指向的文件中。

但实际上使用这程方法有两个问题:

  • Log 文件增长得很大

  • 程序运行很慢
    因为程序要把日志写到 Flash 上,而读写 Flash 是比较慢的,可以想办法证mtrace 的日志输出在终端窗口,由终端软件记录下来。
    如: export MALLOC_TRACE=/dev/stdout
    有些设备没有 stdout ,可以设置 export MALLOC_TRACE=/proc/self/fd/1 来实现

    另外:mtrace 函数内,试图根据调用 malloc 的代码指针,解析出对应的函数,这会导致进程的速讳莫如深变得很慢。

1.5.2 malloc() 和 free() 钩子函数

在glibc 中,提供了 malloc 、free、realloc、memalign 的钩子函数。
可以按照钩子函数的原型,定义自已的函数,并在glibc 中设置相应的钩子函数,这样glibc 在处理内存相关的函数时,会调用钩子函数,从而获得相应的信息。


1.5.3 化整为零法

一般对守护进程才会花大力气去检测泄漏。对于进程仙存的优化,首先需要考虑争取其不是守护进程,或者简化其守护的部分。

将某些相对独立、对内存影响较大的部分,分离出来通过进程来实现,这样业务逻辑完成后,其所占用的内存会随进程的销毁而得到释放。

因此,可以考虑将守护进程中,很多相庆的模块采用子进程的方式来实现,从而减少守护进程本身的复结性。


1.5.4 Dmalloc

dmalloc 是一个开源工具,其具体使用方法如下:

  1. 从 www.dmalloc.com 上下载dmalloc 的源代码。
  2. 按照嵌入式平台的要求对其进行编译,生成静态链接库 libdmalloc.a
  3. 在需要检测的源代码中,包含 dmalloc.h: #include <dmalloc.h>
  4. 重新编译程序代码,gcc -ldmalloc -o program code.c
  5. 在嵌入式设备中,设置环境变量,定义log 的输出文件
    export DMALLOC_options=log=logname, debug=0x3
  6. 运行程序,待程序退出后,会将内存信息输出到日志文件中。

dmalloc 的原理:
在这里插入图片描述
dmalloc 通过宏定义,将代码中的malloc 、realloc 等函数转到自身定义的函数中,从而获得了控制权,运用自身的代码去跟踪和检测堆内存的分配和释放。

dmalloc 的优点是:

  • 运行简单、能够将内存分配和代码行对应起来。

其局限是:

  • 需要修改所有使用 malloc 等函数的文件,对于大项目来说,使用起来非常麻烦。
  • 必须拥有源码。
  • 只能等待进程运行结束后才能获得结果,这对守护进程不适用。
  • 虽然能够获得内存分配的日志记录,精确到具体文件具体行,但是没有如何调用到该函数的栈信息。
  • 使用dmalloc 将增强系统的健状性,但不能完全检测出所有的内存错误。

1.5.5 Valgrind

Valgrind 是一款非常强大的产品,它可以帮助程序员检测无效的内存分配、访问未初始化的内存、内存泄漏等。

Valgrind 包含一个核心,它提供了一个虚拟的CPU 运行程序,还有一系列的工具,它们完成调试、剖析和 一些类似的任务。

它的一个优点在于,它是对程序二进制代码进行跟踪、调试,不必修改代码、重新编译等,使用起来极其方便。

valgrind官网:http://valgrind.org。

valgrind 包含几个标准的工具,分别如下:

1.5.5.1 memcheck

memcheck 控测程序中的内存管理存在的问题。
它检查所有对应内存的读写操作,并截取所有的malloc /new/free/delete 调用,

因此,memcheck 工具能够控测以下问题:
(1)使用未初始化的内存。
(2)读/写 已结被释放的内存。
(3)读/写 内存越界。
(4)读/写 不恰当的内存栈空间。
(5)内存泄漏。
(6)使用 malloc/ new/ new[] 和 free / delete / delete[] 不匹配。


1.5.5.2 cachegrind

cachegrind 是一个 Cache 剖析器。
它模拟执行 CPU 中的 L1、D1 和 L2 Cache,因此它能很精确地指出代码中的Cache 未命中。

如果需要,它可以打印出Cache 中未命中的次数,内存引用和发生 Cache 未命中的每一行代码、每一个函数、每一个模块 和 整个程序的摘要。
如果追求更细致的信息,它可以打印出每一行机器码的未命中次数。

在X86 和 ADM64 上,cachegrind 通过 CPUID 自动探测机器的Cache 配置,所以存多数情况下它不再需要更多的配置信息了。


1.5.5.3 valgrind

valgrind 查找多线程程序中的竞争数据。
它可以查找那些被多个线程访问但没有使用同步锁的内存地址。
这表示这些地址在多线程访问的时候没有进行同步,很可能会引起很难查找的时序问题。

valgrind 使用方法如下:
valgrind --tool=tool_name program_name

比如,对ls -l 命令做内存检查,只需要执行下面的命令就可以了:
valgrind --tool=memcheck ls -l

如果要检查内存泄漏,只需要增回 --leak-check=yes 就 可以了,如下:
valgrind --tool=memcheck --leak-check=yes ls -l


不同工具间加入的代码变化非常大。在每个作用域的末尾,memcheck 加入代码检查每个片内存的访问和进行值计算,代码大小至少增加 12 倍,运行速度要比平时慢 25-50 倍。


valgrind 模拟程序中的每一条指令执行,因此检查工具和剖析工具不仅对你的应用程序,还对共享库、GNU C库、X的客户端都起作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

"小夜猫&小懒虫&小财迷"的男人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值