基本概念

指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

A memory leak is a particular type of unintentional memory consumption by a computer program where the program fails to release memory when no longer needed. This condition is normally the result of a bug in a program that prevents it from freeing up memory that it no longer needs.This term has the potential to be confusing, since memory is not physically lost from the computer. Rather, memory is allocated to a program, and that program subsequently loses the ability to access it due to program logic flaws.

针对 C/C++ 开发

对于C和C++这种没有Garbage Collection 的语言来讲,我们主要关注两种类型的内存泄漏:

  1. 堆内存泄漏(Heap leak) 堆内存指的是程序运行中根据需要分配通过 malloc,realloc,new等从堆中分配的一块内存,再是完成后必须通过调用对应的free或者delete删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak.

  2. 系统资源泄露(Resource Leak) 主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。

解决方案

问题难点

  1. 编译器不能发现这些问题。
  2. 运行时才能捕获到这些错误,这些错误没有明显的症状,时隐时现。
  3. 对于手机等终端开发用户来说,尤为困难。

解决方案

良好的编码习惯

使用了内存分配的函数,一旦使用完毕,记得要使用其想用的函数释放掉。

Heap memory: malloc/remalloc --> free new/ new[] --> delete/delete[] (尤其注意 数组对象的内存泄露,使用 delete[]) GlobalAlloc --> GlobalFree

重载 newdelete

主要思路是将分配的内存以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表,其中记录了内存泄露的文件,所在文件的行数以及泄露的大小。下面的代码是一个简单的内存管理器。

//========================file: MemCheck.h======================================//
// file: MemCheck.h
#include <cstddef>

//辅助结构体,记录内存分配的信息
struct MemIns{
    void *pMem;     //分配的内存指针
    int m_nSize;     //分配的大小
    char m_szFileName[256];     //文件名
    int m_nLine;     //所在行数
    MemIns *pNext;     //指向下一个记录
};

//类定义
class MemManager{
public:
    MemManager();
    ~MemManager();
    static MemManager* GetInstance(); //获取MemManager对象的实例
    void Append(MemIns *pMemIns);    //追加记录
    void Remove(void *ptr);            //删除记录
    void Dump();    //遍历记录链表,输出内存泄露信息
private:
    MemIns *m_pMemInsHead;    //分配记录链表的头指针
    int m_nTotal;    //记录分配的总大小
};

// 重载运算符 new, new[], delete, delete[]
void *operator new(size_t size,const char* szFile, int nLine);
void operator delete(void *ptr,const char* szFile, int nLine);
void operator delete(void *ptr);
void *operator new[] (size_t size,const char* szFile,int nLine);
void operator delete[](void *ptr,const char* szFile, int nLine);
void operator delete[](void *ptr);


//========================file: memoryCheck.cpp======================================//
// file: memoryCheck.cpp
#include "MemCheck.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>

// 构造函数,初始化内存分配记录的链表头,以及总大小 
MemManager::MemManager()
{
    m_pMemInsHead = NULL;
    m_nTotal = 0;
}

//析构函数
MemManager::~MemManager()
{ }

//追加记录
void MemManager::Append(MemIns *pMemIns)
{
    pMemIns->pNext = m_pMemInsHead;        //追加的记录的 next 指针 指向当前头指针
    m_pMemInsHead = pMemIns;    //更新头指针
    m_nTotal += m_pMemInsHead->m_nSize;    //更新分配的大小
}

void MemManager::Remove(void *ptr)
{
    MemIns *pCur = m_pMemInsHead;    //记录当前头指针
    MemIns *pPrev = NULL;        
    while(pCur){    //如果当前指针不为空
        if(pCur->pMem == ptr){    //当前分配记录中分配的内存地址与当前要释放的内存地址相同
            if(pPrev){    
                //要删除的记录不在链表的头部, 将前一个记录的next指针指向当前指针的下一个位置
                pPrev->pNext = pCur->pNext;
            }else{    
                m_pMemInsHead = pCur->pNext;    //将头指针向后移动一位
            }
            m_nTotal-=pCur->m_nSize; //更新分配的大小
            free(pCur);    //释放内存
            break;
        }
        //当前分配记录中分配的内存地址与当前要释放的内存地址不相同,则记录当前指针位置以及前一个记录的位置,寻找要释放的内存地址
        pPrev = pCur;
        pCur = pCur->pNext;
    }
}

//检索链表,输出内存泄露信息
void MemManager::Dump()
{
    MemIns * pp = m_pMemInsHead;
    //输出记录中没有被释放的记录
    if(pp)
    {
        printf("Opps, Memory leak found.\n");
        while(pp){
            printf("At file \"%s:%d\", leak size:%d \n", pp->m_szFileName, pp->m_nLine, pp->m_nSize);
            pp = pp->pNext;
        }
    }else{
        printf("Great, No Memory Leak found.\n");
    }

}

void PutEntry(void *ptr,int size,const char*szFile, int nLine)
{
    MemIns * p = (MemIns *)(malloc(sizeof(MemIns)));    //新建一个记录
    if(p){
        //填充记录值
        strcpy(p->m_szFileName,szFile);    
        p->m_nLine = nLine;
        p->pMem = ptr;
        p->m_nSize = size;
        MemManager::GetInstance()->Append(p);    //追加至记录链表
    }
}

void RemoveEntry(void *ptr)
{
    //移除记录值
    MemManager::GetInstance()->Remove(ptr);
}

void *operator new(size_t size,const char*szFile, int nLine)
{
    void * ptr = malloc(size);        //分配内存
    PutEntry(ptr,size,szFile,nLine);    //添加一个记录
    return ptr;    //返回分配的地址
}

void operator delete(void *ptr)
{
    RemoveEntry(ptr);    //删除记录
    free(ptr);    //释放内存
}

void operator delete(void *ptr, const char * file, int line)
{
    RemoveEntry(ptr);    //删除记录
    free(ptr);    //释放内存
}

void* operator new[] (size_t size,const char* szFile, int nLine)
{
    void * ptr = malloc(size);    //分配内存
    PutEntry(ptr,size,szFile,nLine);    //添加记录
    return ptr;    //返回分配的地址
}

void operator delete[](void *ptr)
{
    RemoveEntry(ptr);    //删除记录
    free(ptr);    //释放内存
}

void operator delete[](void *ptr,const char*szFile, int nLine)
{
    RemoveEntry(ptr);    //删除记录
    free(ptr);    //释放内存
}

#define new new(__FILE__,__LINE__)  //重定义new宏

MemManager m_memTracer; //定义一个全局内存管理对象

MemManager *MemManager::GetInstance()
{
    return &m_memTracer; //获取一个内存管理对象的实例
}


//测试用例
int main()
{
    int *plen = new int;
    *plen=10;
    //delete plen;
    char *pstr=new char[35];
    strcpy(pstr,"hello memory leak");
    //delete[] pstr;
    m_memTracer.Dump();
    return 0;
}

//输出结果:
/*
Opps, Memory leak found.
At file "MemCheck.cpp:139", leak size:35 
At file "MemCheck.cpp:136", leak size:4 
*/

智能指针

由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete。程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见。

用智能指针便可以有效缓解这类问题,常见的智能指针包括:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost:: intrusive_ptr 七种。

对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象访问智能指针原来的方法则使用“.”操作符。

访问智能指针包含的裸指针则可以用 get() 函数。由于智能指针是一个对象,所以 if (my_smart_object)永远为真,要判断智能指针的裸指针是否为空,需要这样判断:if (my_smart_object.get())。

智能指针包含了 reset() 方法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递一个对象,则智能指针会释放当前对象,来管理新传入的对象。

//示例辅助类
class Simple {
 public:
  Simple(int param = 0) {
    number = param;
    std::cout << "Simple: " << number << std::endl; 
  }

  ~Simple() {
    std::cout << "~Simple: " << number << std::endl;
  }

  void PrintSomething() {
    std::cout << "PrintSomething: " << info_extend.c_str() << std::endl;
  }

  std::string info_extend;
  int number;
};
std::auto_ptr
  • std::auto_ptr 属于 STL,当然在 namespace std 中,包含头文件 #include<memory> 便可以使用。
  • std::auto_ptr 能够方便的管理单个堆内存对象。

    void TestAutoPtr() 
    {
      std::auto_ptr<Simple> my_memory(new Simple(1));   // 创建对象,输出:Simple:1
      if (my_memory.get()) {                            // 判断智能指针是否为空
          my_memory->PrintSomething();              // 使用 operator-> 调用智能指针对象中的函数
          my_memory.get()->info_extend = "Addition";   // 使用 get() 返回裸指针,然后给内部对象赋值
          my_memory->PrintSomething();                 // 再次打印,表明上述赋值成功
          (*my_memory).info_extend += " other";        // 使用 operator* 返回智能指针内部对象,然后用“.”调用智能指针对象中的函数
          my_memory->PrintSomething();                 // 再次打印,表明上述赋值成功
        }
    }                    // my_memory 栈对象即将结束生命期,析构堆对象 Simple(1)
    /*
    执行结果为:
    Simple: 1
    PrintSomething:
    PrintSomething: Addition
    PrintSomething: Addition other
    ~Simple: 1
    */
    

    上述为正常使用 std::auto_ptr 的代码,一切似乎都良好,无论如何不用我们显示使用该死的 delete 了。其实好景不长,我们看看如下的另一个例子:

    void TestAutoPtr2() {
    std::auto_ptr<Simple> my_memory(new Simple(1));
    if (my_memory.get()) {
      std::auto_ptr<Simple> my_memory2;   // 创建一个新的 my_memory2 对象
      my_memory2 = my_memory;             // 复制旧的 my_memory 给 my_memory2
      my_memory2->PrintSomething();       // 输出信息,复制成功
      my_memory->PrintSomething();        // 崩溃
    }
    }
    

    最终如上, 代码导致崩溃,如上代码时绝对符合 C++ 编程思想的,居然崩溃了,跟进 std::auto_ptr 的源码后,我们看到,罪魁祸首是“my_memory2 = my_memory”,这行代码,my_memory2 完全夺取了 my_memory 的内存管理所有权,导致 my_memory 悬空,最后使用时导致崩溃。 所以,使用 std::auto_ptr 时,绝对不能使用“operator=”操作符。作为一个库,不允许用户使用,确没有明确拒绝,多少会觉得有点出乎预料。 看完 std::auto_ptr 好景不长的第一个例子后,让我们再来看一个:

    void TestAutoPtr3() {
    std::auto_ptr<Simple> my_memory(new Simple(1));
    
    if (my_memory.get()) {
      my_memory.release();
    }
    }
    /*
    执行结果为:
    Simple: 1
    */
    

    看到什么异常了吗?我们创建出来的对象没有被析构,没有输出"~Simple: 1",导致内存泄露。当我们不想让 my_memory 继续生存下去,我们调用 release() 函数释放内存,结果却导致内存泄露(在内存受限系统中,如果my_memory占用太多内存,我们会考虑在使用完成后,立刻归还,而不是等到 my_memory 结束生命期后才归还)。 正确的代码应该为:

    void TestAutoPtr3() {
    std::auto_ptr<Simple> my_memory(new Simple(1));
    if (my_memory.get()) {
      Simple* temp_memory = my_memory.release();
      delete temp_memory;
    }
    }
    //或
    void TestAutoPtr3() {
    std::auto_ptr<Simple> my_memory(new Simple(1));
    if (my_memory.get()) {
      my_memory.reset();  // 释放 my_memory 内部管理的内存
    }
    }
    

    原来 std::auto_ptr 的 release() 函数只是让出内存所有权,这显然也不符合 C++ 编程思想。

总结:std::auto_ptr 可用来管理单个对象的对内存,但是,请注意如下几点:

  • 尽量不要使用“operator=”。如果使用了,请不要再使用先前对象。
  • 记住 release() 函数不会释放对象,仅仅归还所有权。
  • std::auto_ptr 最好不要当成参数传递(读者可以自行写代码确定为什么不能)。
  • 由于 std::auto_ptr 的“operator=”问题,有其管理的对象不能放入 std::vector 等容器中。

使用一个 std::auto_ptr 的限制还真多,还不能用来管理堆内存数组,这应该是你目前在想的事情吧,我也觉得限制挺多的,哪天一个不小心,就导致问题了。由于 std::auto_ptr 引发了诸多问题,一些设计并不是非常符合 C++ 编程思想,所以引发了下面 boost 的智能指针,boost 智能指针可以解决如上问题。

boost::scoped_ptr

boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件#include<boost/smart_ptr.hpp> 便可以使用。 boost::scoped_ptr 跟 std::auto_ptr 一样,可以方便的管理单个堆内存对象,特别的是,boost::scoped_ptr 独享所有权,避免了 std::auto_ptr 恼人的几个问题。

void TestScopedPtr() {
  boost::scoped_ptr<Simple> my_memory(new Simple(1));
  if (my_memory.get()) {
    my_memory->PrintSomething();
    my_memory.get()->info_extend = "Addition";
    my_memory->PrintSomething();
    (*my_memory).info_extend += " other";
    my_memory->PrintSomething();

    my_memory.release();           // 编译 error: scoped_ptr 没有 release 函数
    std::auto_ptr<Simple> my_memory2;
    my_memory2 = my_memory;        // 编译 error: scoped_ptr 没有重载 operator=,不会导致所有权转移
  }
}

首先,我们可以看到,boost::scoped_ptr 也可以像 auto_ptr 一样正常使用。但其没有 release() 函数,不会导致先前的内存泄露问题。 其次,由于 boost::scoped_ptr 是独享所有权的,所以明确拒绝用户写“my_memory2 = my_memory”之类的语句,可以缓解 std::auto_ptr 几个恼人的问题。 由于 boost::scoped_ptr 独享所有权,当我们真真需要复制智能指针时,需求便满足不了了,如此我们再引入一个智能指针,专门用于处理复制,参数传递的情况,这便是如下的 boost::shared_ptr。

boost::shared_ptr

boost::shared_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件 #include<boost/smart_ptr.hpp> 便可以使用。

在上面我们看到 boost::scoped_ptr 独享所有权,不允许赋值、拷贝,boost::shared_ptr 是专门用于共享所有权的, 由于要共享所有权,其在内部使用了引用计数。boost::shared_ptr 也是用于管理单个堆内存对象的。

void TestSharedPtr(boost::shared_ptr<Simple> memory) {  // 注意:无需使用 reference (或 const reference)
  memory->PrintSomething();
  std::cout << "TestSharedPtr UseCount: " << memory.use_count() << std::endl;
}

void TestSharedPtr2() {
  boost::shared_ptr<Simple> my_memory(new Simple(1));
  if (my_memory.get()) {
    my_memory->PrintSomething();
    my_memory.get()->info_extend = "Addition";
    my_memory->PrintSomething();
    (*my_memory).info_extend += " other";
    my_memory->PrintSomething();
  }

  std::cout << "TestSharedPtr2 UseCount: " << my_memory.use_count() << std::endl;
  TestSharedPtr(my_memory);
  std::cout << "TestSharedPtr2 UseCount: " << my_memory.use_count() << std::endl;

  //my_memory.release();// 编译 error: 同样,shared_ptr 也没有 release 函数
}
/*
执行结果为:
Simple: 1
PrintSomething:
PrintSomething: Addition
PrintSomething: Addition other
TestSharedPtr2 UseCount: 1
PrintSomething: Addition other
TestSharedPtr UseCount: 2
TestSharedPtr2 UseCount: 1
~Simple: 1
*/

boost::shared_ptr 也可以很方便的使用。并且没有 release() 函数。关键的一点,boost::shared_ptr 内部维护了一个引用计数,由此可以支持复制、参数传递等。

boost::shared_ptr 提供了一个函数 use_count() ,此函数返回 boost::shared_ptr 内部的引用计数。查看执行结果,我们可以看到在 TestSharedPtr2 函数中,引用计数为 1,传递参数后(此处进行了一次复制),在函数TestSharedPtr 内部,引用计数为2,在 TestSharedPtr 返回后,引用计数又降低为 1。当我们需要使用一个共享对象的时候,boost::shared_ptr 是再好不过的了。

boost::scoped_array

boost::scoped_array 属于 boost 库,定义在 namespace boost 中,包含头文件 #include 便可以使用。

boost::scoped_array 便是用于管理动态数组的。跟 boost::scoped_ptr 一样,也是独享所有权的。

void TestScopedArray() {
      boost::scoped_array<Simple> my_memory(new Simple[2]); // 使用内存数组来初始化
      if (my_memory.get()) {
        my_memory[0].PrintSomething();
        my_memory.get()[0].info_extend = "Addition";
        my_memory[0].PrintSomething();
        (*my_memory)[0].info_extend += " other";     // 编译 error,scoped_ptr 没有重载 operator*
        my_memory[0].release();                    // 同上,没有 release 函数
        boost::scoped_array<Simple> my_memory2;
        my_memory2 = my_memory;       // 编译 error,同上,没有重载 operator=
      }
    }

boost::scoped_array 的使用跟 boost::scoped_ptr 差不多,不支持复制,并且初始化的时候需要使用动态数组。另外,boost::scoped_array 没有重载“operator*”,其实这并无大碍,一般情况下,我们使用 get() 函数更明确些。

boost::shared_array

boost::shared_array 属于 boost 库,定义在 namespace boost 中,包含头文件 #include 便可以使用。

由于 boost::scoped_array 独享所有权,显然在很多情况下(参数传递、对象赋值等)不满足需求,由此我们引入 boost::shared_array。跟 boost::shared_ptr 一样,内部使用了引用计数

void TestSharedArray(boost::shared_array<Simple> memory) {  // 注意:无需使用 reference (或 const reference)
  std::cout << "TestSharedArray UseCount: " << memory.use_count() << std::endl;
}

void TestSharedArray2() {
  boost::shared_array<Simple> my_memory(new Simple[2]);
  if (my_memory.get()) {
    my_memory[0].PrintSomething();
    my_memory.get()[0].info_extend = "Addition 00";
    my_memory[0].PrintSomething();
    my_memory[1].PrintSomething();
    my_memory.get()[1].info_extend = "Addition 11";
    my_memory[1].PrintSomething();
    //(*my_memory)[0].info_extend += " other";  // 编译 error,scoped_ptr 没有重载 operator*
  }
  std::cout << "TestSharedArray2 UseCount: " << my_memory.use_count() << std::endl;
  TestSharedArray(my_memory);
  std::cout << "TestSharedArray2 UseCount: " << my_memory.use_count() << std::endl;
}
/*
执行结果为:
Simple: 0
Simple: 0
PrintSomething:
PrintSomething: Addition 00
PrintSomething:
PrintSomething: Addition 11
TestSharedArray2 UseCount: 1
TestSharedArray UseCount: 2
TestSharedArray2 UseCount: 1
~Simple: 0
~Simple: 0
*/

跟 boost::shared_ptr 一样,使用了引用计数,可以复制,通过参数来传递。

boost::weak_ptr

boost::weak_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件 #include 便可以使用。

在讲 boost::weak_ptr 之前,让我们先回顾一下前面讲解的内容。似乎 boost::scoped_ptr、boost::shared_ptr 这两个智能指针就可以解决所有单个对象内存的管理了,这儿还多出一个 boost::weak_ptr,是否还有某些情况我们没纳入考虑呢?回答:有。首先 boost::weak_ptr 是专门为 boost::shared_ptr 而准备的。有时候,我们只关心能否使用对象,并不关心内部的引用计数。boost::weak_ptr 是 boost::shared_ptr 的观察者(Observer)对象,观察者意味着 boost::weak_ptr 只对 boost::shared_ptr 进行引用,而不改变其引用计数,当被观察的 boost::shared_ptr 失效后,相应的 boost::weak_ptr 也相应失效。

void TestWeakPtr() {
      boost::weak_ptr<Simple> my_memory_weak;
      boost::shared_ptr<Simple> my_memory(new Simple(1));

      std::cout << "TestWeakPtr boost::shared_ptr UseCount: " << my_memory.use_count() << std::endl;
      my_memory_weak = my_memory;
      std::cout << "TestWeakPtr boost::shared_ptr UseCount: " << my_memory.use_count() << std::endl;
}
/*
执行结果为:
Simple: 1
TestWeakPtr boost::shared_ptr UseCount: 1
TestWeakPtr boost::shared_ptr UseCount: 1
~Simple: 1
*/

我们看到,尽管被赋值了,内部的引用计数并没有什么变化,当然,读者也可以试试传递参数等其他情况。现在要说的问题是,boost::weak_ptr 到底有什么作用呢?从上面那个例子看来,似乎没有任何作用,其实 boost::weak_ptr 主要用在软件架构设计中,可以在基类(此处的基类并非抽象基类,而是指继承于抽象基类的虚基类)中定义一个 boost::weak_ptr,用于指向子类的 boost::shared_ptr,这样基类仅仅观察自己的 boost::weak_ptr 是否为空就知道子类有没对自己赋值了,而不用影响子类 boost::shared_ptr 的引用计数,用以降低复杂度,更好的管理对象。

boost::intrusive_ptr

boost::intrusive_ptr属于 boost 库,定义在 namespace boost 中,包含头文件 #include 便可以使用。 boost::intrusive_ptr,这是一种插入式的智能指针,内部不含有引用计数,需要程序员自己加入引用计数,不然编译不过(⊙﹏⊙b汗)。个人感觉这个智能指针没太大用处,至少我没用过。有兴趣的朋友自己研究一下源代码。

总结

如上讲了这么多智能指针,有必要对这些智能指针做个总结:

  1. 在可以使用 boost 库的场合下,拒绝使用 std::auto_ptr,因为其不仅不符合 C++ 编程思想,而且极容易出错。
  2. 在确定对象无需共享的情况下,使用 boost::scoped_ptr(当然动态数组使用 boost::scoped_array)。
  3. 在对象需要共享的情况下,使用 boost::shared_ptr(当然动态数组使用 boost::shared_array)。
  4. 在需要访问 boost::shared_ptr 对象,而又不想改变其引用计数的情况下,使用 boost::weak_ptr,一般常用于软件框架设计中。
  5. 最后一点,也是要求最苛刻一点:在你的代码中,不要出现 delete 关键字(或 C 语言的 free 函数),因为可以用智能指针去管理。

各种检测工具

  1. ccmalloc-Linux和Solaris下对C和C++程序的简单的使用内存泄漏和malloc调试库。
  2. Dmalloc-Debug Malloc Library.
  3. Electric Fence-Linux分发版中由Bruce Perens编写的malloc()调试库。
  4. Leaky-Linux下检测内存泄漏的程序。
  5. LeakTracer-Linux、Solaris和HP-UX下跟踪和分析C++程序中的内存泄漏。
  6. MEMWATCH-由Johan Lindh编写,是一个开放源代码C语言内存错误检测工具,主要是通过gcc的precessor来进行。
  7. Valgrind-Debugging and profiling Linux programs, aiming at programs written in C and C++.
  8. KCachegrind-A visualization tool for the profiling data generated by Cachegrind and Calltree.
  9. Leak Monitor-一个Firefox扩展,能找出跟Firefox相关的泄漏类型。
  10. IE Leak Detector (Drip/IE Sieve)-Drip和IE Sieve leak detectors帮助网页开发员提升动态网页性能通过报告可避免的因为IE局限的内存泄漏。
  11. Windows Leaks Detector-探测任何Win32应用程序中的任何资源泄漏(内存,句柄等),基于Win API调用钩子。
  12. SAP Memory Analyzer-是一款开源的Java内存分析软件,可用于辅助查找JAVA程序的内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于Eclipse RCP(Rich Client Platform),可以下载RCP的独立版本或者Eclipse的插件。
  13. DTrace-即动态跟踪Dynamic Tracing,是一款开源软件,能在Unix类似平台运行,用户能够动态检测操作系统内核和用户进程,以更精确地掌握系统的资源使用状况,提高系统性能,减少支持成本,并进行有效的调节。
  14. IBM Rational PurifyPlus-帮助开发人员查明C/C++、托管.NET、Java和VB6代码中的性能和可靠性错误。PurifyPlus 将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。

  15. Parasoft Insure++-针对C/C++应用的运行时错误自动检测工具,它能够自动监测C/C++程序,发现其中存在着的内存破坏、内存泄漏、指针错误和I/O等错误。并通过使用一系列独特的技术(SCI技术和变异测试等),彻底的检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为Microsoft Visual C++的一个插件运行。

  16. Compuware DevPartner for Visual C++ BoundsChecker Suite-为C++开发者设计的运行错误检测和调试工具软件。作为Microsoft Visual Studio和C++ 6.0的一个插件运行。
  17. Electric Software GlowCode-包括内存泄漏检查,code profiler,函数调用跟踪等功能。给C++和.Net开发者提供完整的错误诊断,和运行时性能分析工具包。
  18. Compuware DevPartner Java Edition-包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块。
  19. Quest JProbe-分析Java的内存泄漏。
  20. ej-technologies JProfiler-一个全功能的Java剖析工具,专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中。JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问题。4.3.2注册码:A-G666#76114F-1olm9mv1i5uuly#0126
  21. BEA JRockit-用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最高的性能。
  22. SciTech Software AB .NET Memory Profiler-找到内存泄漏并优化内存使用针对C#,VB.NET,或其它.Net程序。
  23. YourKit .NET & Java Profiler-业界领先的Java和.NET程序性能分析工具。
  24. AutomatedQA AQTime-AutomatedQA的获奖产品performance profiling和memory debugging工具集的下一代替换产品,支持Microsoft, Borland, Intel, Compaq 和 GNU编译器。可以为.NET和Windows程序生成全面细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net 1.0,1.1,2.0,3.0和Windows 32/64位应用程序。
  25. JavaScript Memory Leak Detector-微软全球产品开发欧洲团队(Global Product Development- Europe team, GPDE) 发布的一款调试工具,用来探测JavaScript代码中的内存泄漏,运行为IE系列的一个插件

内存泄露 VS 内存溢出

内存溢出(out of memory): 程序在申请内存时,没有足够的内存空间供其使用。一般发生内存溢出时,程序讲无法进行,强制终止。 常见的溢出:

  • 内存分配未成功,却使用了它。 解决:使用之前,检测指针是否为null
  • 内存分配虽然成功,但是尚未初始化就引用它。 解决:确保初始化
  • 内存分配成功并且已经初始化,但操作越过了内存的边界。 解决: 编程时注意
  • 使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”。 解决:编程时注意

内些泄露(memory leak): 程序在申请内存后,无法释放已经申请的内存空间,内存泄露的积累将导致内存溢出。

results matching ""

    No results matching ""