如何初步使用valgrind工具来检测内存泄露,堆栈空间,未初始化变量问题

简介: 如何初步使用valgrind工具来检测内存泄露,堆栈空间,未初始化变量问题

官方概念

Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件  (plug-in),利用内核提供的服务完成各种特定的内存调试任务。

Valgrind 体系结构

Valgrind包括如下一些工具:

◼  Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够
发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已
经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。

◼  Callgrind。它主要用来检查程序中函数调用过程中出现的问题。

◼  Cachegrind。它主要用来检查程序中缓存使用出现的问题。

◼  Helgrind。它主要用来检查多线程程序中出现的竞争问题。

◼  Massif。它主要用来检查程序中堆栈使用中出现的问题。

◼  Extension。可以利用core提供的功能,自己编写特定的内存调试工具。

写一个例子,内存泄露

sample.c

#include <stdio.h>
#include <stdlib.h>
void fun( )
{
    int *p = (int *)malloc(10*sizeof(int));
    p[11] = 0; // 此处越界
    free(p);
}
int main(void)
{
    fun(); 
    return 0;
}

gcc -g -o sample sample.c    -- 使用-g 加上调试

valgrind --tool=memcheck ./sample

valgrind 会提示 哪一行出了问题

1.  这是一个对内存的非法写操作,非法写操作的内存是4 bytes。

2.  发生错误时的函数堆栈,以及具体的源代码行号。

3.  非法写操作的具体地址空间。


在例如,把上面的例子,malloc的内存,不free

#include <stdio.h>
#include <stdlib.h>
void fun( )
{
    int *p = (int *)malloc(10*sizeof(int));
    p[11] = 0; // 此处越界
    //free(p);
}
int main(void)
{
    fun(); 
    return 0;
}

1块内存被分配,0块内存被释放   -- 内存泄露

 

第2个demo,未初始化的变量

badloop.c

#include <stdio.h>
int main(void)
{
    int a[5];
    int i, s;//s为局部变量,且为初始化
    a[0] = a[1] = a[2] = a[3] = a[4] = 0;
    for (i = 0; i < 5; i++)
    {
        s += a[i]; //使用了未初始化的变量,结果未知
    }
    if (s == 888)
    {
        printf("sum is %d\n", s);
    }
    return 0;
}

gcc -g -o badloop badloop.c

valgrind --tool=memcheck ./badloop

动态内存管理错误

mem.c

#include <stdio.h>
#include <stdlib.h>
int main(void)   
{   
  char * p = (char *)malloc(10);
  char *pt = p;
  for(int i = 0;i<10;i++)
  {
    p[i] = i;
  }
  delete p ;  //使用c++ 的 delete 去释放p ,delete 是兼容c语言的 释放内存机制的
  *pt = 'o';
  free(pt);
     return 0;   
 }

g++ -g -o mem mem.c

valgrind --tool=memcheck ./mem

常见的内存动态管理错误包括:

◼  申请和释放不一致

由于  C++ 兼容  C,而  C 与  C++ 的内存申请和释放函数是不同的,因此在  C++ 程

序中,就有两套动态内存管理函数。一条不变的规则就是采用  C 方式申请的内存就用

C 方式释放;用  C++ 方式申请的内存,用  C++ 方式释放。也就是用  malloc/alloc/realloc

方式申请的内存,用  free 释放;用  new 方式申请的内存用  delete 释放。在上述程序

中,用  malloc 方式申请了内存却用  delete 来释放,虽然这在很多情况下不会有问题,

但这绝对是潜在的问题。

◼  申请和释放不匹配

申请了多少内存,在使用完成后就要释放多少。如果没有释放,或者少释放了就是

内存泄露;多释放了也会产生问题。上述程序中,指针p和pt指向的是同一块内存,却

被先后释放两次。

◼  释放后仍然读写

本质上说,系统会在堆上维护一个动态内存链表,如果被释放,就意味着该块内存

可以继续被分配给其他部分,如果内存被释放后再访问,就可能覆盖其他部分的信息,

这是一种严重的错误。

 

c++中的内存泄露问题

badleak1.cpp

#include <iostream>
#include <vector>
class Item
{
 public:
    Item()
    {
        printf("构造Item\n");
    }
    ~Item()
    {
        printf("析构Item\n");
    }
};
int main(void)
{
    std::vector<Item*> vItem;
    for(int i= 0; i < 2; i++)
    {
        Item *item = new Item();
        vItem.push_back(item);
    }
    return 0;
}

g++ -g -o badleak badleak.cpp -std=c++11

valgrind ./badleak

new 出来的内存,没有delete 释放 ,出现内存泄露,valgrind检测出来了

 

看看下面这个demo,使用智能指针修饰一下

 

badleak2.cpp

g++ -g -o badleak badleak2.cpp -std=c++11

valgrind ./badleak2

#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Item
{
    public:
    Item()
    {
        printf("构造Item\n");
    }
    ~Item()
    {
        printf("析构Item\n");
    }
};
int main(void)
{
    vector<shared_ptr<Item>> vitem;
  for(int i = 0 ;i<2;i++){
    shared_ptr<Item> item = shared_ptr<Item>(new Item());
        //也可以 shared_ptr<Item> item = make_shared<Item>();
    vitem.push_back(item);
  }
    return 0;
}

问题解决

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关文章
|
14天前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
36 6
|
23天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
140 9
|
23天前
|
监控 JavaScript 前端开发
如何检测和解决 JavaScript 中内存泄漏问题
【10月更文挑战第25天】解决内存泄漏问题需要对代码有深入的理解和细致的排查。同时,不断优化和改进代码的结构和逻辑也是预防内存泄漏的重要措施。
36 6
|
26天前
|
Web App开发 缓存 JavaScript
如何检测和解决闭包引起的内存泄露
闭包引起的内存泄露是JavaScript开发中常见的问题。本文介绍了闭包导致内存泄露的原因,以及如何通过工具检测和代码优化来解决这些问题。
|
2月前
|
C语言 Android开发 C++
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
本文介绍了使用MTuner软件进行Qt MinGW编译程序的内存泄漏检测的方法,提供了MTuner的下载链接和测试代码示例,并通过将Debug程序拖入MTuner来定位内存泄漏问题。
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
|
1月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
51 2
|
1月前
|
Web App开发 开发者
|
1月前
|
C++
析构造函数就是为了释放内存,就是在局部指针消失前释放内存,拷贝构造函数就是以构造函数为模块,在堆里面新开一块,同一个变量在堆里面的地址
本文讨论了C++中构造函数和析构函数的作用,特别是它们在管理动态内存分配和释放中的重要性,以及如何正确地实现拷贝构造函数以避免内存泄漏。
38 2
|
29天前
|
缓存 监控 Java
内存泄漏:深入理解、检测与解决
【10月更文挑战第19天】内存泄漏:深入理解、检测与解决
45 0
|
1月前
|
设计模式 Java Android开发
安卓应用开发中的内存泄漏检测与修复
【9月更文挑战第30天】在安卓应用开发过程中,内存泄漏是一个常见而又棘手的问题。它不仅会导致应用运行缓慢,还可能引发应用崩溃,严重影响用户体验。本文将深入探讨如何检测和修复内存泄漏,以提升应用性能和稳定性。我们将通过一个具体的代码示例,展示如何使用Android Studio的Memory Profiler工具来定位内存泄漏,并介绍几种常见的内存泄漏场景及其解决方案。无论你是初学者还是有经验的开发者,这篇文章都将为你提供实用的技巧和方法,帮助你打造更优质的安卓应用。