推入队列时的内存泄漏

时间:2012-10-08 22:16:04

标签: c++ c memory-leaks queue

我一直在为我的数据网络课程开展项目工作,遇到内存泄漏,但我不明白为什么会这样。

顺便说一下,我知道C和C ++有很多混合,但我对此无能为力,它基于类代码,我无法修改它,我知道这不是一个好的这样做的方法,我需要使用char *作为必要条件。

我的程序是多线程的,我处理这个结构:

typedef struct packetQueue
{
    char* buf;
    int length;

    packetQueue()
    {
        buf = nullptr;
        length = 0;
    }

    packetQueue(char* buffer, int len)
    {
        length = len;
        buf = new char[length + 1];
        memcpy(buf, buffer, len);
        buf[length] = '\0';
    }

    packetQueue(const packetQueue& other)
    {
        length = other.length;

        if (other.buf)
        {
            buf = new char[length + 1];
            memcpy(buf, other.buf, length);
            buf[length] = '\0';
        }
        else
        {
            buf = nullptr;
        }
    }

    packetQueue& operator=(const packetQueue& that)
    {
        if (this == &that)
        {
            return *this;
        }

        delete[] buf;

        length = that.length;

        if (that.buf)
        {
            buf = new char[length + 1];
            memcpy(buf, that.buf, length);
            buf[length] = '\0';
        }
        else
        {
            buf = nullptr;
        }
        return *this;
    }

    ~packetQueue()
    {
        delete[] buf;
        buf = nullptr;
    }

} PACKET;

在我的带有两个参数的构造函数中,我执行了这个赋值,因为我的队列的推送执行了我的结构的深层复制,就像我有我的复制构造函数一样,我已经处理过了。所以,我有一个线程(我一个接一个地测试,VLD结果只适用于这个)。

DWORD _stdcall PHY_in_Thread(void* data)
{
    int numbytes, counter = 0;

    SOCKET hostSocket = *(SOCKET*) data; // Socket where the host receives

    struct sockaddr_in si_recvfrom;
    struct sockaddr_storage their_addr;
    socklen_t addr_len;

    addr_len = sizeof their_addr;

    while ( 1 )
{
    char* recBuf = new char[BUFLEN + 1];
    // Checks if it's any buffer on the socket to be processed
    if ( (numbytes = recvfrom(hostSocket, recBuf, BUFLEN, 0, (sockaddr*) &si_recvfrom, &addr_len)) == -1)
    {
        cerr << "Could not receive datagram." << endl;
        delete[] recBuf;
        closesocket(hostSocket);            
        WSACleanup();
        exit(0);
    }
    recBuf[numbytes] = '\0'; // append NULL to the end of the string

    char* temporalBuffer = new char[numbytes - CHECKSUM_MAX_SIZE + 1];
    memcpy(temporalBuffer, recBuf, numbytes - CHECKSUM_MAX_SIZE);
    temporalBuffer[numbytes - CHECKSUM_MAX_SIZE] = '\0';

    char extractedChecksum[CHECKSUM_HEX_SIZE + 1];
    DWORD crcBuffer = crc32buf(temporalBuffer, numbytes- CHECKSUM_MAX_SIZE); // Calculates the CRC32 checksum
    _snprintf(extractedChecksum, 8 , "%08lX", crcBuffer); // Prints the string in a buffer
    extractedChecksum[CHECKSUM_HEX_SIZE] = '\0';

    delete[] temporalBuffer;

    string strExtractedChecksum = extractedChecksum; // Copies the array in a string
    transform(strExtractedChecksum.begin(), strExtractedChecksum.end(), strExtractedChecksum.begin(), upper); // Uppercase the string

    // Array for store the checksum of the packet
    char readChecksum[CHECKSUM_MAX_SIZE + 1];

    // Store the checksum of the packet in local variable
    memcpy( readChecksum, &recBuf[numbytes - CHECKSUM_MAX_SIZE], CHECKSUM_MAX_SIZE);    
    readChecksum[CHECKSUM_MAX_SIZE] = '\0';

    std::stringstream stream;
    string strReadChecksum;
    for (int i = 0; i < CHECKSUM_MAX_SIZE; i++ )
    {
        int number = static_cast<int>(readChecksum[i]); // Casts every character of the checksum array

        if ( readChecksum[i] <= -1 ) // In case the int value it's negative adds the constant value to make that recognizable
        {
            number += 256;
        }

        // Convert the decimal number in a hex representation
        stream.str("");
        stream << hex << number;

        if ( stream.str().length() < 2 ) // In case it's a number less than 10, adds a 0 at the beginning
        {
            strReadChecksum += "0" +  stream.str();
        }
        else
        {
            // Working out the presentation of the number
            strReadChecksum += stream.str();
        }
    }

    std::transform(strReadChecksum.begin(), strReadChecksum.end(), strReadChecksum.begin(), upper); // Uppercase the string
    strReadChecksum[CHECKSUM_HEX_SIZE] = '\0';

    cout << "[PI] Frame #" << counter <<" received ("<< numbytes <<" bytes). " << endl;
    if ( !strcmp(strReadChecksum.c_str(), extractedChecksum) ) // Checks if the CRC are equal
    {
        cout << "[CRC] Checksum OK: 0x" << extractedChecksum << endl;
    }
    else
    {
        cout << "[CRC] Checksum failure: 0x" << extractedChecksum << endl;
    }

    // Push the packet in the MAC_in_queue to be processed
    MAC_in_queue.push(PACKET(recBuf, numbytes));
    recBuf = nullptr;
    counter++;

            break;   // Just for test one packet
}

MAC_in_queue.clear();

return 0;

}

但是当我执行这个线程并向这个线程发送一些东西以便存储在这个队列中时会产生泄漏。在这个执行中,只有一个项目可以使事情变得简单。

---------- Block 29 at 0x0068F718: 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 (240): MSVCP110D.dll!Concurrency::details::_Concurrent_queue_base_v4::_Internal_move_push
    d:\program files (x86)\microsoft visual studio 11.0\vc\include\concurrent_queue.h (581): Host.exe!Concurrency::concurrent_queue<packetQueue,std::allocator<packetQueue> >::push + 0xF bytes
    d:\users\silex rpr\documents\visual studio 2012\projects\project3\hoster\host.cpp (638): Host.exe!PHY_in_Thread + 0x3D bytes
    0x7474339A (File and line number not available): kernel32.dll!BaseThreadInitThunk + 0x12 bytes
    0x76EC9EF2 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x63 bytes
    0x76EC9EC5 (File and line number not available): ntdll.dll!RtlInitializeExceptionChain + 0x36 bytes
  Data:
    00 00 00 00    01 00 00 00    60 F8 68 00    80 00 00 00     ........ `.h.....
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........
    CD CD CD CD    CD CD CD CD    CD CD CD CD    CD CD CD CD     ........ ........

但是我不明白这个数据泄漏的位置,我希望我能说清楚。

事先谢谢

5 个答案:

答案 0 :(得分:9)

while ( 1 ) PHY_in_Thread()循环开始时,您分配了recBuf

char* recBuf = new char[BUFLEN + 1];

但是在循环体的末尾,你错过了释放它;你只是因为泄漏内存 recBuf = nullptr;任务。

相反,请尝试在循环体末端正确删除recBuf

delete [] recBuf;
recBuf = nullptr;

答案 1 :(得分:2)

在分配&#34;的副本之前,您的赋值运算符不会释放PACKET中的旧内容。&#34;因此,如果您覆盖某些容器代码中的条目,则覆盖项目所持有的缓冲区将会泄漏。这是一个泄漏,但当然我无法证明这是你所看到的泄漏。

答案 2 :(得分:1)

您已使用此命令在while(1)循环的开头分配了recBuf:

char* recBuf = new char[BUFLEN + 1];

然后将它传递给临时变量PACKET(recBuf,numbytes),它只适用于一行代码的范围:

MAC_in_queue.push(PACKET(recBuf, numbytes));

现在,假设MAC_in_queue初始化为:

concurrent_queue<PACKET> MAC_in_queue

这意味着您将同时使用复制构造函数和operator =。因为你没有在这两个命令中释放为buf分配的内存,所以你有泄漏。

请阅读示例与您类似的this article。了解他们如何实现Copy构造函数和operator =

答案 3 :(得分:1)

在我看来,你的packetQueue没有做太多的事情(除了内存管理,它没有正常工作)。

您可以使用标准容器实现您的类。假设您不想要类的引用计数实现,我们需要排除std :: string,但您可以使用std :: vector。例如:

class PacketQueue
{
public:
    PacketQueue() : buf_() {}
    PacketQueue(char* buffer, int len) : buf_(buffer,buffer + len) {}
    //here your function to return the '\0' terminated buffer
    //and all the other stuff that you need
private:
    std::vector<char> buf_;
};

请注意,默认:复制构造函数,赋值运算符和析构函数都可以,您不需要实现任何操作。基本上,内存管理封装在std :: vector中。

这也是强大的异常安全保障。你的代码不是。

答案 4 :(得分:1)

我只能在我面前推荐其他人。 泄漏是在你的while循环中,分配recBuf,但没有适当的删除。 详情:

//Allocates memory,ok
char* recBuf = new char[BUFLEN + 1];

//free memory when exits, ok
if ( (numbytes = recvfrom(...)) == -1)
{
    cerr << "Could not receive datagram." << endl;
    delete[] recBuf;
    ...
}

//do something with it

//And here is the problem
MAC_in_queue.push(PACKET(recBuf, numbytes));
//With this you call this constructor
// packetQueue(char* buffer, int len)
// which allocates the same amount of memory, and copies the contents of recBuf
//So, there is a +1 memory allocation

// This is not deallocate memory :)
recBuf = nullptr;

我建议将recBuf内存分配移到循环之外,并在最后删除它(使用delete [] recBuf)

delete[] recBuf;
MAC_in_queue.clear();

,有了这个,你不必每次都进行针刺,因此要快一些。 或者如果你喜欢这种方式,你应该插入这样的代码:

 delete[] recBuf;
 recBuf = nullptr;