泄漏记忆的方式不同

时间:2011-07-03 05:33:54

标签: c++ memory-leaks

内存泄漏的基本概念是代码执行期间新/删除操作的不匹配,可能是由于错误的编码实践,也可能是在跳过删除操作时出现错误。

但最近我在一次采访中被问到一个问题,关于记忆可以泄漏的其他方式。 我没有答案。它是什么?

7 个答案:

答案 0 :(得分:16)

常见的动态内存问题是:

  • 使用new进行动态内存分配,而不是使用delete取消分配。
  • 使用new[]分配动态内存,并使用delete取消分配。
  • 动态内存分配new并将其与free取消分配。
  • 动态内存分配malloc并将其与delete取消分配。

除了内存泄漏/内存损坏之外,最后3种情况还会导致可怕的 Undefined Behavior

其他一些潜在的内存泄漏导致我可以回忆起的情况是:

  • 如果指向动态分配的内存区域的指针在被取消分配之前重新分配了新值,则会导致 dangling pointer 并且内存泄漏。

代码示例:

char *a = new[128];
    char *b = new[128];
    b = a;
    delete[]a;
    delete[]b; // will not deallocate the pointer to the original allocated memory.

- STL容器中的指针

更常见且经常遇到的情况是,存储指向STL容器中动态分配类型的指针。请务必注意,只有当STL容器不是指针类型 时,STL容器才会删除所包含的对象 。 在删除容器本身之前,必须显式迭代容器并删除每个包含的类型。不这样做会导致内存泄漏 Here 就是这种情况的一个例子。

- 非虚拟基类析构函数问题

删除指向Base类的指针,该指针指向堆上派生类的任何动态分配对象。这会导致未定义的行为。

代码示例:

class MyClass
{
    public:
    virtual void doSomething(){}
}; 
class MyClass2 : public MyClass 
{ 
    private:  
        std::string str;  
    public: MyClass2( std::string& s) 
    {  
        str=s; 
    }  
    virtual void doSomething(){}
};  

int main()
{  
     std::str hello("hello"); 
     MyClass * p = new MyClass2(hello);  
     if( p ) 
     { 
        delete p;  
     } 
     return 0;
}

在示例中,只调用析构函数MyClass ::〜MyClass(),并且永远不会调用MyClass2 :: ~MyClass2()。为了适当的解除分配,我们需要,

MyClass::virtual ~MyClass(){}

- 在delete指针上调用void

代码示例:

void doSomething( void * p ) 
{
    //do something interesting
    if(p)
       delete p; 
}

int main()
{
    A* p = new A();
    doSomething(p);
    return 0;
}

如上例所示,在delete指针上调用void将导致内存泄漏和未定义行为。

答案 1 :(得分:4)

作为面试问题,面试官可能一直在寻找比教科书新/删除不匹配更广泛的观点。

任何超出最后一点所需的内存都可以被视为“泄露”。最终可以通过代码中的删除进行手动释放此内存,使这些泄漏成为临时泄漏,而不是使用不匹配的新/删除操作符时遇到的永久性泄漏。在“泄漏”持续的持续时间内,净效应是相同的。您正在减少程序其他部分的可用资源(内存)。

在垃圾收集代码中,如果继续保持对不再需要的对象的任何引用,则会认为内存泄露,从而阻止垃圾回收器回收它。如果无限期地保留不需要的对象,则会在垃圾收集代码中创建永久性泄漏。

答案 2 :(得分:3)

  1. 分配内存 new/new[]/malloc而不是释放它
  2. 将内存分配给某个指针 并意外地覆盖它,例如p = new int; p = new int[1];
  3. 在空中分配记忆。即new int[100];cout<<(*new string("hello"))<<endl;

答案 3 :(得分:2)

总是我最喜欢的,“非泄漏的内存泄漏”,你的程序正确地保留了指向已分配内存的指针,但是(无论出于什么原因)从来没有实际释放它们指向的内存:

// Maybe not technically a memory leak, but it might as well be one
static vector<const char *> strings;

void StoreTheString(const char * str)
{
   strings.push_back(strdup(str));
}

这是令人讨厌的,因为内存泄漏检测器程序不会将其识别为内存泄漏(因为指针仍然可以访问),但是程序仍会耗尽内存直至崩溃。

即使在像Java这样的垃圾收集语言中也会出现这种问题。

答案 4 :(得分:1)

碎片可能是导致内存不足的问题之一。 经过长时间运行程序后,可能会导致内存碎片化。

答案 5 :(得分:1)

另一种情况是使用引用计数智能指针,如boost::shared_ptr,通常被认为是“消除内存泄漏”,但是您创建了循环引用。

How to avoid memory leak with shared_ptr?

答案 6 :(得分:0)

我使用带有重载operator new和delete的Boehm垃圾收集器,它适用于任何使用它编译的类,但是对于std :: string和一些STL容器也是如此,即使变量是垃圾收集类的成员。在我意识到之前,有一些内存泄漏。值得庆幸的是,它提供了一个垃圾收集分配器。

我还记得一辆用Java编程的自动驾驶汽车,每20到40分钟坠毁一次。它收集了关于沿路上遇到的各种灌木丛和垃圾的物体检测信息,将它们订阅到某个队列,......并且从未将它们删除。

*:看看我在那里做了什么:D