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;

2 つのパラメーターを持つコンストラクターでは、キューのプッシュによって構造体のディープ コピーが実行され、コピー コンストラクターがあり、既にそれを処理しているように、その割り当てを行います。したがって、スレッドが 1 つあります (1 つずつテストしており、VLD の結果はこの 1 つのみです)。

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;

}

しかし、このスレッドを実行し、このスレッドに何かを送信してこのキューに格納すると、リークが発生します。この実行では、物事を単純にするための項目は 1 つだけです。

---------- 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

代入演算子は、「それ」のコピーを割り当てる前に、PACKET 内の古い内容を解放しません。そのため、コンテナ コードのエントリを上書きすると、上書きされたアイテムが保持するバッファがリークします。それはリークですが、もちろん、あなたが見ているのがリークであることを証明することはできません.

于 2012-10-11T13:58:21.450 に答える
1

次のコマンドを使用して、while(1)ループの先頭にrecBufを割り当てました。

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

次に、それを一時変数PACKET(recBuf、numbytes)に渡します。これは、1行のコードのスコープでのみ機能します。

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

ここで、MAC_in_queueが次のように初期化されたと仮定します。

concurrent_queue<PACKET> MAC_in_queue

これは、コピーコンストラクターとoperator=を同時に使用することを意味します。このコマンドのいずれでもbufに割り当てられたメモリを解放しないため、リークが発生します。

あなたと同じような例があるこの記事を読んでください。彼らがどのようにコピーコンストラクターと演算子を実装したかを見る=

于 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 に答える