3

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

顺便说一句,我知道 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     ........ ........

但是我不明白这些数据泄漏在哪里,我希望我说清楚了。

预先感谢

4

5 回答 5

9

在您分配的while ( 1 )循环开始时:PHY_in_Thread()recBuf

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

但是在循环体的末尾,你错过了释放它;您只是由于 recBuf = nullptr;分配而泄漏了内存。

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

delete [] recBuf;
recBuf = nullptr;
于 2012-10-12T14:54:05.873 回答
2

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

于 2012-10-11T13:58:21.450 回答
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 分配的内存,所以存在泄漏。

请阅读这篇文章,其中包含与您类似的示例。看看他们如何实现 Copy 构造函数和 operator =

于 2012-10-12T04:18:00.167 回答
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 中。

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

于 2012-10-12T15:36:34.417 回答
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;
于 2012-10-16T05:50:59.323 回答