5

OutOfMemoryException が発生しないように、プロセスで使用可能なメモリを取得しようとしています。インターネットを検索したところ、メモリを使用しているが利用できない方法の例がいくつか見つかりました。

ユースケースを提供しましょう...

一括挿入を行っているプロセスがあります (SqlBulkCopy を使用)。メソッドに aDataTableを渡しています。失敗時にプロセスを再試行できるようにする必要がWriteToServerあるため、a を使用できません。DataReader私が最初に考えたのは、一度に挿入する任意の行数、たとえば 50,000 行を選択することでした。しかし、これはデータを知らない一般的なプロセスです。列の数も各行のデータ量もわかりません。そのため、に行を追加しているときにメモリを監視し、メモリが不足しそうにDataTableなったときにそれを投稿できると考えていましたSqlBulkCopy

これは有効なアプローチですか、それともより良い方法がありますか?
これが有効なアプローチである場合、利用可能なメモリの量を決定するためにどの関数を使用しますか?

これまでの私のコードは次のとおりです...これAvailableMemoryIsLowは、私が判断する方法を理解できないものです。

// m_buffer is a read-once cache (implements IDataReader) that pulls 
// data from an external source as needed so it uses very little memory.
// My original implementation just used m_buffer as the parameter of 
// WriteToServer but now I have to add retry logic into the process.

DataTable dataTable = new DataTable(m_tableName);
foreach (DataField d in m_buffer.GetColumns())
    dataTable.Columns.Add(new DataColumn(d.FieldName, d.FieldType));

while (m_buffer.Read())
{
    DataRow row = dataTable.NewRow();
    for (int i = 0; i < m_buffer.FieldCount; i++)
        row[i] = m_buffer.GetValue(i);

    dataTable.Rows.Add(row);

    // How do I determine AvailableMemoryIsLow
    if (rowCount++ >= 50000 || AvailableMemoryIsLow)
    {
        PutDataIntoDatabase(dataTable);
        dataTable.Clear();
        rowCount = 0;
    }
}

if (dataTable.Rows.Count > 0)
    PutDataIntoDatabase(dataTable);
4

3 に答える 3

3

明らかに、このコードを 32 ビット マシンで実行していないと、この問題は発生しません。一般に、利用可能な仮想メモリ空​​間 (2 ギガバイト) のほぼすべてをプログラムに消費させることは合理的ではありません。常に存在する OOM の危険性を除けば、処理しているデータの種類は「ライブ データ」であり、RAM にマップされる可能性が非常に高くなります。使用可能な RAM のほぼすべてを要求するプログラムは、そのプログラムの動作、オペレーティング システム、およびそのマシンで実行されるその他のプロセスにかなり悪影響を及ぼします。

オペレーティング システムに、プロセスが必要とするものとファイル システム キャッシュ用に予約するものとの間で RAM を割り当てる方法の選択を開始するように強制します。この種の選択は、常に RAM からページング ファイルにデータを強制的に格納することになります。これにより、書き込み時と、プロセスが RAM に戻す必要があるときの両方で、操作が大幅に遅くなる可能性があります。「スラッシング」と呼ばれるオペレーティング システムのパフォーマンスの問題。

これを行わないでください。RAM に大量のデータをダンクしても、プログラムが速くなることはありません。遅くなります。32 ビット オペレーティング システムで消費する RAM の量の妥当な上限は、500 メガバイト近くです。正確にその制限に達する必要はありません。行をカウントするだけで十分です。

于 2012-12-05T17:07:08.120 に答える
1

あなたは、割り当てられたメモリの量を示すメソッドを見つけたと述べました

    GC.GetTotalMemory(false);

はそのような方法の 1 つです (すでに見つけていると思います)。

MSDN のドキュメントから指摘したいことが 1 つあります。

現在割り当てられていると考えられるバイト数を取得します

これは、GC.GetTotalMemory メソッドのドキュメントの一番上にあります。上記のフレーズの思考という言葉を指摘したいと思います。質問で言及されているように、割り当てられた量を見つける方法を知っていることはわかっていますが、C# がマネージ言語であることを説明するためにこれを取り上げます。メモリの使用量と消費量は抽象化されており、GC メソッドでさえ、プロセスで何が起こっているかについて漠然とした考えを提供するだけです。メモリ レベルを手動で操作するのは危険で信頼性がないように思えます。

元のアプローチを使用することをお勧めしますが、作業している列の数に関係なく、メモリ不足の例外が発生する可能性が非常に低いレベルにバッチサイズを戻すことをお勧めします。数万ではなく、数百、おそらく数千と考えてください。大規模なバッチによって得られるパフォーマンスの向上は、たとえそれを検出しようとしても、これらのレベルでのメモリの問題のリスクが上回る可能性があります。別の回答で言及されているパフォーマンスツールは、バッチサイズがどうあるべきか、それが問題であるかどうかを判断するための優れた方法です.

于 2012-12-05T16:32:35.910 に答える
0

問題は、さまざまな種類の「リソース」がたくさんあることです。いずれも「OutOfMemoryException」で現れる可能性があります。

ただし、おそらく最善の策はGC.GetTotalMemory(false).

さらに良いアプローチは、JetBrains dotTraceや RedGate ANTSなどのツールを入手することです。

私見では ...

PS:

SQL 一括コピーを行っている場合は、必ず EnableStreaming を設定してください。

于 2012-12-05T16:05:55.170 に答える