使用 Char* 查找内存泄漏的原因

Posted

技术标签:

【中文标题】使用 Char* 查找内存泄漏的原因【英文标题】:Finding Reason For Memory Leaking With Char* 【发布时间】:2012-09-15 10:14:12 【问题描述】:

我在一个处理一些 char* 指针的项目中工作,使用 char* 而不是 std::string 是该类的必要条件,所以...

我有这个结构定义和这个队列:

typedef struct packetQueue

    char* buf;
    int length;

    packetQueue()
    
        buf = new char[];
        length = 0;
    

     PACKET;

并发::concurrent_queue IP_in_queue;

我有这个缓冲区:

char sendBuf[MSG_SIZE + sizeof (IP_PACKET_HEADER_T) + 1]; // String to be send

以及我的新缓冲区的结构:

PACKET ipQueue;

然后我用这个填充我的缓冲区:

// Concatenates the header with sended message
memcpy(sendBuf, (void*)&sendHeader, sizeof(sendHeader));

memcpy(&sendBuf[sizeof(sendHeader)], readMessage, sendHeader.length);

ipQueue.buf = sendBuf;
ipQueue.length = packetSize;

然后我将数据包推送到队列中 IP_in_queue.push(ipQueue); // 将缓冲区推送到 IP_in_queue 中

这是我的循环以防万一:

while ( 1 )

    // Get the user input
    cout << "> ";
    cin.getline (buf, BUFLEN);

    IP_PACKET_HEADER_T sendHeader; // Store the header to be send
    PACKET ipQueue;

    char* fakeIPAddressDst, *readMessage; 

    delay = atoi(strtok (buf," ")); // Takes the first delay value
    fakeIPAddressDst = strtok (NULL, " "); // Stores the IP Address
    readMessage = strtok (NULL, " "); // Stores the sended message

    Sleep(delay); // Sleep the miliseconds defined

    // Fills the header with the data neccesary data
    sendHeader.DIP = inet_addr(fakeIPAddressDst);
    sendHeader.SIP = inet_addr(initAddress.fakeIpAddress);
    sendHeader.length = getStringLength(readMessage) + 1;
    packetSize = sizeof( sendHeader ) + sendHeader.length; // Defines the size of the packet to be send

    // Concatenates the header with sended message
    memcpy(sendBuf, (void*)&sendHeader, sizeof(sendHeader));
    memcpy(&sendBuf[sizeof(sendHeader)], readMessage, sendHeader.length);

    ipQueue.buf = sendBuf;
    ipQueue.length = packetSize;

    numbytes = packetSize; // The number of bytes of sended buffer

    char sendedString[BUFLEN + 1]; // Variable for stores the data
    IP_PACKET_HEADER_T readHeader; // To store the header for showing the information

    // Print out the content of the packet
    // Copy from buf to the header
    memcpy( (void*)&readHeader, ipQueue.buf, sizeof( IP_PACKET_HEADER_T));
    // Copy message part
    memcpy( sendedString, &ipQueue.buf[sizeof(IP_PACKET_HEADER_T)], numbytes - sizeof(IP_PACKET_HEADER_T));
    // Append \0 to the end
    sendedString[numbytes - sizeof(IP_PACKET_HEADER_T)] = '\0';

    // Save the IP information of the packet in a struct for print on the screen
    struct in_addr fakeAddrHost;
    fakeAddrHost.s_addr = readHeader.SIP;

    // Print the neccesary data
    cout << "[IN] DST: " << fakeIPAddressDst << endl; // Fake IP address of the destination
    cout << "[IN] SRC: " << inet_ntoa(fakeAddrHost) << endl; // Fake IP address of the host
    cout << "[IN] MSG: " << sendedString << endl ; // Message to send

    IP_in_queue.push(ipQueue); // Push the buffer in the IP_in_queue

我知道此过程中存在内存泄漏,但我不确定。 当我推送我的数据包时,buf 指针一直指向我的 sendBuf,对吗?因为分配是这样做的,但是如果我在推送程序崩溃后删除了 ipQueue 中的指针。我不得不说,在我将该结构推入队列后,另一个线程会尝试弹出该结构,显然如果我删除我的 ipQueue 指针,我将丢失我的缓冲区,那么我该如何避免这种内存泄漏?

谢谢

编辑:

使用buf = nullptr定义的内存泄漏

---------- Block 1 at 0x0068BB30: 264 bytes ----------
  Call Stack:
    d:\program files (x86)\microsoft visual studio 11.0\vc\include\concurrent_queue.h (402): Host.exe!Concurrency::concurrent_queue<packetQueue,std::allocator<packetQueue> >::_Allocate_page + 0xF bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\concurrent_queue.cpp (113): MSVCP110D.dll!Concurrency::details::_Micro_queue::_Push + 0xD bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\concurrent_queue.cpp (232): MSVCP110D.dll!Concurrency::details::_Concurrent_queue_base_v4::_Internal_push
    d:\program files (x86)\microsoft visual studio 11.0\vc\include\concurrent_queue.h (566): Host.exe!Concurrency::concurrent_queue<packetQueue,std::allocator<packetQueue> >::push + 0xF bytes
    d:\users\silex rpr\documents\visual studio 2012\projects\project2\project2\host.cpp (802): Host.exe!main
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (536): Host.exe!__tmainCRTStartup + 0x19 bytes
    f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c (377): Host.exe!mainCRTStartup
    0x7662339A (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0x12 bytes
    0x77179EF2 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x63 bytes
    0x77179EC5 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x36 bytes

【问题讨论】:

哇...你有一个相当混乱的 C 和 C++ 混合... 这不是 C,您使用的是 C++ 编译器。 C 中的结构不能有方法和构造函数 是的,对不起,我认为我的标签是正确的,再次抱歉 【参考方案1】:

首先;这不是 C,您使用的是 C++ 编译器。 C 中的结构不能有方法和构造函数,并且 newdelete 不存在。

其次,您在构造函数中为 buf 分配内存,但随后...

ipQueue.buf = sendBuf;

这是一个泄漏。每次拨打new 时,您都需要拨打delete。你用new分配buf,但从来没有在上面调用delete,这样内存就泄露了。

我认为没有理由在此处分配 buf。只需将其设置为 null。

typedef struct packetQueue

    char* buf;
    int length;

    packetQueue()
        : buf(nullptr), length(0)  
 PACKET;

顺便说一句,这是 C 和 C++ 的非常糟糕的组合。这是你们老师教你们的吗?

【讨论】:

嗯,好吧,我会试试的,是的,我一直在问他,他说这还不错,他发布了一些他用来混合 std::string 和 C- 的例子string,stringstream,它总是与他的想法混合在一起,总的来说,这段代码几乎是基于他的一个例子,但我知道我不应该以这种方式混合这两种语言,但他不听任何人的话。 @oscar.rpr:只要你知道。听起来这位老师并没有用这两种语言编写太多现实世界的代码。 使用你的结构定义它一直在泄漏内存,我在主帖中留下了它向我展示的 vld。 @oscar.rpr:那你又泄了。我不能说我分析了所有的代码,只是显而易见的。你new你必须delete的所有东西 @oscar.rpr:你也没有给我们一个完整的程序。有些变量甚至没有被声明。【参考方案2】:

你有一个类(结构),它有一个分配内存的构造函数,没有释放它的析构函数,所以你会发生内存泄漏。

您还公开了buf 成员并分配给它;所以你的班级无法控制是否应该释放内存。但是在第一次分配给buf之前,需要释放构造函数中分配的内存。

要做到这一点,您需要将buf 字段设为私有并添加析构函数、复制构造函数和赋值运算符(可能还有访问函数)。不过,你仍然不会是异常安全的。

【讨论】:

即使使用析构函数也会泄漏,因为他将 buf 设置为新值。 @EdS。是的——修改和扩展。【参考方案3】:

您遇到的问题是您没有查看复制构造和复制分配:当您将对象推送到std::vector&lt;T&gt; 时,它会被复制,而带有std::vector&lt;T&gt; 的对象可能会使用分配移动。默认生成的复制构造函数和复制赋值只是复制或分配各自的成员,即,无论何时使用任何一个副本,您最终都会得到两个指向同一 buf 的对象。第一个被销毁的将使用delete[] buf;,而所有其他的将有一个无法再次删除的陈旧指针。也就是说,你想在packetQueue 中添加三个方法:

struct packetQueue

    packetQueue(packetQueue const& other);             // copy constructor: copies the content
    packetQueue& operator= (packetQueue const& other); //copy assignment: updates the content
    ~packetQueue()                                     // destructor: release the memory
    void swap(packetQueue& other);                     // swap the content
    // other members
;

为了在复制分配中利用复制构造和销毁,我发现有一个swap() 成员很有用,因为这很容易实现并且可以实现一个漂亮、简单的复制分配:

void packetQueue::swap(packetQueue& other) 
    std::swap(this->buf, other.buf);
    std::Swap(this->size, other.size);

packetQueue& packetQueue::operator= (packetQueue const& other) 
    packetQueue(other).swap(*this);
    return *this;

【讨论】:

但是例如,我的程序一直从一个向量复制到另一个向量,继续复制所有内容对我的性能来说会有点糟糕吗?是的,我知道四法则,有很多名字,但在这种情况下我不确定。 处理资源的方法有三种:最简单的就是我上面描述的复制。如果您不能复制,您可以参考计数您的资源,这可能更有效但更难正确。最后,如果您可以使用 C++ 2011,您可以移动对象而不是复制它们。这需要玩一些技巧,这些技巧也很重要。就我个人而言,如果这确实太慢了,我会从最简单的方法开始处理问题。

以上是关于使用 Char* 查找内存泄漏的原因的主要内容,如果未能解决你的问题,请参考以下文章

如何防止java中的内存泄漏

重新仪器“内存泄漏”分析,其他工具是不是提供更多信息以找到泄漏的根本原因?

如何使用 Chrome 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏内存膨胀和频繁的垃圾回收

Android内存泄漏查找和解决

常见的内存泄漏原因及解决方法

记一次内存溢出查找分析文档