不同的内存泄漏方式

Different ways of leaking memory

本文关键字:泄漏 方式 内存      更新时间:2023-10-16

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

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

常见的动态内存问题有:

  • 使用new进行动态内存分配,而不使用delete进行解除分配
  • new[]动态分配内存,用delete解除分配内存
  • 动态内存分配CCD_ 5并将其与CCD_ 6解除分配
  • 动态内存分配CCD_ 7并用CCD_

除了内存泄漏/内存损坏外,最后三种情况还会导致可怕的未定义行为

我还记得其他一些可能导致内存泄漏的场景:

  • 如果指向动态分配内存区域的指针在被释放之前被重新分配了一个新值,则会导致悬挂指针和内存泄漏

代码示例:

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容器只有在所包含的对象不是指针类型时才拥有删除该对象的所有权
在删除容器本身之前,必须显式地遍历容器并删除每个包含的类型。不这样做会导致内存泄漏
这里就是这样一个场景的例子。

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

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

一个代码示例:

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(){}

-在void指针上调用delete

代码示例:

void doSomething( void * p ) 
{
    //do something interesting
    if(p)
       delete p; 
}
int main()
{
    A* p = new A();
    doSomething(p);
    return 0;
}

如上例所示,在void指针上调用delete将导致内存泄漏和Undefined Behavior。

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

任何持续超过最后一点的内存都可以被视为"泄漏"。通过在代码中进一步删除,这些内存最终可能会被手动释放,从而使这些泄漏成为临时性的,而不是使用不匹配的新/delete运算符时遇到的永久性泄漏。然而,在"泄漏"持续的时间内,净效果是相同的。您正在减少程序其他部分的可用资源(内存)量。

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

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

我最喜欢的是"无泄漏内存泄漏",在这种情况下,程序正确地保留了指向已分配内存的指针,但(无论出于何种原因)从未真正腾出时间来释放它们指向的内存:

// 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这样的垃圾收集语言中。

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

如何使用shared_ptr避免内存泄漏?

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

我使用带有重载运算符new和delete的Boehm垃圾收集器,它非常适用于使用它编译的任何类,但在std::string和一些STL容器中也会失败,即使变量是垃圾收集类的成员。在我意识到之前有一些内存泄漏。谢天谢地,它提供了一个垃圾回收分配器。

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

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