内存泄漏专题(3)内存泄漏调试神器valgrind

简介: 内存泄漏专题(3)内存泄漏调试神器valgrind

上一章中,我们提出了排查内存泄漏是否存在的几种思路,但只是停留在理论上,本章就这几种思路提供具体的解决方案。在本章节,将以具体的例子的方式来演示如何定位内存泄漏的具体位置。

追踪内存泄漏的工具有很多,网上一搜能找到一大片,但针对不同的系统,均有不同的玩法,且有些东西,如果不是实际实战一把,真到了用的时候,仍然会眼高手低,不知如何下手。笔者就自己在实际工作中常用的几种手段,具体来说明一下一些常用的调试手段。

主要用到的技术有:

  • valgrind
  • mtrace内存追踪
  • eBPF调试工具:bcc
  • 手动hook
  • C++运算符重载

本章主要介绍内存泄漏调试神器:valgrind

valgrindLinux系统下一个检测内存泄露的神器。相信很多C/C++的初学者对该工具都不陌生,很多内存检测的入门教程都会提到该工具。事实上,在Linux环境下,这个工具的确非常好用。

valgrind之所以能够检测出内存泄漏,主要得益于其内部包含的一款叫做Memcheck的工具。该工具建立了两个表,Valid-Value表和Valid-Address表。Valid-Value负责记录进程地址空间中每个字节在 寄存器中的值是否有效。Valid_Address表负责记录该地址是否能够被读写。

当要读写内存中的某个字节时,首先检测出这个字节对应的Value是否有效,如果无效,则memcheck报告读写错误。

同时,valgrind提供了一个类似sandbox的虚拟CPU环境,这样当内存中某个字节被加载到真实CPU中去时,该字节同样也会被加载到valgrind提供的虚拟CPU环境中,一旦这个值被读写,那么memcheck就会检查Valid-Value表,如果该值没有被初始化,就会报未初始化内存错误。

使用Memcheck,可以检查很多内存方面的问题,诸如:

  • 对未初始化内存的使用
  • 内存释放后再次被使用
  • 访问非法内存地址
  • 内存泄漏,即指向一块内存的指针永久性地丢失
  • 内存被double free
  • memcpy相关函数中的dstsrc指针重叠

下面,我们就以一个简单的例子来探讨下,如何使用valgrind来检测内存泄露的问题。

首先,valgrind工具需要安装,安装命令如下:

yum install -y valgrind

valgrind的命令行参数解释:

选项 说明
-tool=<name> 运行 valgrind中名为toolname的工具。默认m`emcheck
h –help 显示帮助信息
-version 显示valgrind内核的版本
q –quiet 静默运行,只打印错误信息
v –verbose 更详细的信息, 增加错误数统计
-trace-children=no|yes 跟踪子线程? [no]
-track-fds=no|yes 跟踪打开的文件描述符?[no]
-time-stamp=no|yes 增加时间戳到LOG信息? [no]
-log-fd=<number> 输出LOG到描述符文件 [2=stderr]
-log-file=<file> 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID
-log-file-exactly=<file> 输出LOG信息到 file
-log-file-qualifier=<VAR> 取得环境变量的值来做为输出信息的文件名。 [none]
-log-socket=ipaddr:port 输出LOGsocketipaddr:port
-xml=yes 将信息以xml格式输出,只有memcheck可用
-num-callers=<number> 调用栈层级[12]
error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]
-error-exitcode=<number> 如果发现错误则返回错误代码 [0=disable]
-db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]
-db-command=<command> 启动调试器的命令行选项[gdb -nw %f %p]
-leak-check=no|summary|full 要求对leak给出详细信息? [summary]
-leak-resolution=low|med|high 泄漏检查中合并多少bt[low]
-show-reachable=no|yes 在泄漏检查中显示可达块[no]

接下来,我们以一个具体的例子来说明下如何使用valgrind来检查内存泄漏。示例代码如下:

#include<malloc.h>
int main(void){
void *p1 = malloc(10);
    void *p2 = malloc(20);
    free(p1);
    void *p3 = malloc(30);
    free(p3);
    return 0;
}

运行命令如下:

gcc leak.c -g
valgrind --tool=memcheck --leak-check=full ./a.out

运行后,得到如下结果:

==229181== Memcheck, a memory error detector
==229181== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==229181== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==229181== Command: ./a.out
==229181== 
==229181== 
==229181== HEAP SUMMARY:
==229181==     in use at exit: 20 bytes in 1 blocks
==229181==   total heap usage: 3 allocs, 2 frees, 60 bytes allocated
==229181== 
==229181== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
==229181==    at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==229181==    by 0x40059C: main (leak.c:5)
==229181== 
==229181== LEAK SUMMARY:
==229181==    definitely lost: 20 bytes in 1 blocks
==229181==    indirectly lost: 0 bytes in 0 blocks
==229181==      possibly lost: 0 bytes in 0 blocks
==229181==    still reachable: 0 bytes in 0 blocks
==229181==         suppressed: 0 bytes in 0 blocks
==229181== 
==229181== For lists of detected and suppressed errors, rerun with: -s
==229181== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

我们关注一下摘要里的信息,第9行显示,有3次申请,2次释放,共60字节内存。第11~13行则报告了内存泄漏的信息,说有一次20字节的内存被丢失,具体位置为leak.c的第5行。

通过代码,可以定位到leak.c的第5行为void *p2 = malloc(20),的确p2的内存我们没有释放。

从以上消息中,可以看出valgrind对于内存泄漏的定位还是非常准确的,因此不失为一个内存泄漏调试的好手段。

valgrind也有局限性,首先它需要提前安装,因此对于生产环境几乎不可能线上实时检测;其次,由于其原理是虚拟出一个CPU环境,因此对资源的占用也比较厉害。最后,valgrindLinux环境可用,如果你的应用程序不是运行在Linux环境下,则无法使用,这也算是它的一个最大硬伤。


本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对C/C++课程感兴趣的读者,可以点击链接,查看详细的服务:C/C++Linux服务器开发/高级架构师

目录
相关文章
|
14天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
35 6
|
22天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
138 9
|
2月前
|
Java
在 ArkTS 中,如何有效地进行内存管理和避免内存泄漏?
【9月更文挑战第25天】在ArkTS中,有效进行内存管理并避免内存泄漏的方法包括:及时释放不再使用的资源,如关闭监听器和清理定时器;避免循环引用,通过弱引用打破循环;合理使用单例模式,确保单例对象正确释放;及时处理不再使用的页面和组件,在卸载时清理相关资源。
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
2月前
|
Arthas 监控 Java
监控线程池的内存使用情况以预防内存泄漏
监控线程池的内存使用情况以预防内存泄漏
|
3月前
|
Linux 测试技术 C++
内存管理优化:内存泄漏检测与预防。
内存管理优化:内存泄漏检测与预防。
58 2
|
2月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
72 0
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
380 0
|
27天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
57 1
|
1月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。