0

次のコードを見てください。このコードでは、実行開始時のメモリ使用量は 1020K です。しかし、実行終了時のメモリ使用量は 1144K です。誰かがメモリリークを特定するのを手伝ってくれますか? がfunc()5 回呼び出されると、メモリ使用量は 1500K+ になります。リストを使用しない場合、メモリ使用量は増加しません。

void func();

int _tmain(int argc, _TCHAR* argv[])
{
    func();
    return 0;
}

void func()
{
    list<char*> list1;
    list<char*>::iterator iter;
    char* val;
    for(int i=0; i<100000; i++)
    {
        val = new char[20];
        for(int j=0; j<20;j++)
        {
            val[j] = 'A';
        }
        val[19] = '\0';
        list1.push_back(val);
    }

    iter = list1.begin();
    for(int k=0; k<100000;k++, iter++)
    {
        delete[] *iter;
        *iter = NULL;
    }
    val = NULL;

    list1.clear();
    list1.empty();
}
4

1 に答える 1

3

ここでメモリリークは見られませんが、非常に貧弱なコードがたくさん見られます。

あなたのコードには、あなたが Windows で実行しているというヒントがいくつかあります。水晶玉をじっと見ると、タスク マネージャーをチェックしてメモリ リークを検出しているのが見えます。タスク マネージャーを使用してメモリ リークを検出することは、ブロードソードを使用して手術を行うようなものです。TM は、システムに大きなメモリ リークが存在する可能性があるというヒントを提供できる可能性がありますが、それはプログラムからあまりにもかけ離れていて、あまりにも粗すぎて決定的ではありません。TM を使用してメモリ リークがあるかどうかを判断する代わりに、そのジョブ専用のツールを使用する必要があります。Visual Studioには、このようなツールが組み込まれています。

コードでこれらの組み込み機能を使用すると、次のようになります。

#include <cstdlib>
#include <list>
using std::list;

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>


void func();

int main(int argc, char* argv[])
{
    func();
    return 0;
}

void func()
{
    list<char*> list1;
    list<char*>::iterator iter;
    char* val;
    for(int i=0; i<100000; i++)
    {
        val = new char[20];
        for(int j=0; j<20;j++)
        {
            val[j] = 'A';
        }
        val[19] = '\0';
        list1.push_back(val);
    }

    iter = list1.begin();
    for(int k=0; k<100000;k++, iter++)
    {
        delete[] *iter;
        *iter = NULL;
    }
    val = NULL;

    list1.clear();
    list1.empty();

    _CrtDumpMemoryLeaks();
}

...そうですか:

Detected memory leaks!
Dumping objects ->
{142} normal block at 0x00000000000778C0, 24 bytes long.
 Data: < x       x      > C0 78 07 00 00 00 00 00 C0 78 07 00 00 00 00 00 
{141} normal block at 0x0000000000077840, 16 bytes long.
 Data: <(       h       > 28 F7 1A 00 00 00 00 00 68 F7 1A 00 00 00 00 00 
Object dump complete.

合計 40 バイトの 2 つのメモリ リークが報告されているため、ここでは広範なメモリ リークはありません。さらに、これらはおそらく誤検知であり、CRT による静的メモリ割り当てなどを報告しているため、無視する必要があります。

しかし、私が言ったように、非常に貧弱なコードがたくさんあります。これはメモリ リークの原因ではありませんが、実際のコードでは簡単に発生する可能性があります。

  1. マジックナンバーを多用しています。のようにfor(int i=0; i<100000; i++)、代わりにリストを反復する必要がある場合 ( for( list<char*>::iterator it = list1.begin(); it != list1.end(); ++it))、または少なくともリストに含まれる要素の数を尋ねる ( for( size_t i = 0; i < list1.size(); ++i ))

  2. 動的割り当て/割り当て解除を使用しています。 動的割り当てが本当に必要な場合は代わりに RAII を使用する必要がありますが、可能な限り動的割り当てを完全に避けてください。あなたの場合、 の代わりに を使用list<char*>する必要がありlist<std::string>ます。

  3. 標準ライブラリが提供するアルゴリズムを使用できる場所で、手書きのループを使用しています。あなたが書かないコードは、最も信頼できるコードです。内側のループで char 配列を初期化する代わりに、単に dostd::string s(20,'A')を実行し、外側のループで各リスト メンバーを設定する代わりに、copyorのようなものを使用しますtransform

編集これは、上記の問題のいくつかに対処する関数の再構成されたバージョンです。

#include <string>
using std::string;
#include <algorithm>
using std::generate_n;
#include <iterator>
using std::back_inserter;

void func()
{
    static const size_t NumStrings = 10000;
    typedef list<string> strings;

    // Populate the list
    strings list2;
    generate_n(back_inserter(list2), NumStrings, []() -> string
    {
        static const size_t NumChars = 19;
        static const char InitChar = 'A';
        return string(NumChars, InitChar);
    });

    // Clear the list
    list2.clear();

    // Done
    _CrtDumpMemoryLeaks();
}

への呼び出しgenerate_nは C++11 ラムダを使用しますが、簡単にリファクタリングして、ファンクターまたは別のものを完全に使用することができます。

于 2012-12-11T12:06:10.210 に答える