3

次のコードで発生する悪いメモリ リークが発生しています。

 public void BulkInsert(string tableName, IDataReader reader, String connectionString)
    {
        using (var connection = new SqlConnection(connectionString))
        {
            connection.Open();
            using (var bulkCopy = new SqlBulkCopy(connection))
            {
                bulkCopy.DestinationTableName = tableName;
                bulkCopy.BulkCopyTimeout = 900;

                try
                {
                    bulkCopy.WriteToServer(reader);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    reader.Close();
                }
            }
        }
    }

このコード セグメントは何千回も実行されるため、メモリ不足の例外が発生するのに 1 分しかかかりません。Ants は、これは IDataReader が指す行が GC によって収集されていないためであると報告しています。ただし、次の行をコメントアウトしてもリークはありません。これは、問題をこのコードに分離した方法です。

bulkCopy.WriteToServer(reader);

メモリリークを防ぐ方法について誰か提案がありますか?

前もって感謝します。

コーリング コード:

 var reader = datatable.CreateDataReader();
 BulkInsert(tablename, reader, connectionString);
 reader.Dispose();
 datatable.Dispose();
4

2 に答える 2

4

私は自分の質問に答えるのが絶対に嫌いですが、最終的に解決策を見つけました.

メモリ リークは、提供されたコード セグメントにはまったくありませんでした。実際には、まったくリークではありませんでした...

パフォーマンス分析を見ると、BulkCopy 呼び出しがプログラム全体のボトルネックであることがわかりました。挿入する DataTables を供給する Producer-Consumer パターンがあります。

コードを実行したときに、Memory Profiler が破棄されていない DataTable オブジェクトを表示していると思いました。これらは実際には挿入待ちのキュー テーブルでしたが、テスト データを再利用していたため、キュー テーブルは既に DB に存在していました (そのため、既に挿入されているように見えました)。

BulkCopy の行をコメントアウトすることで、実際にボトルネックを取り除いていました。DataTable はすぐに破棄されたため、Memory Profiler に問題は表示されませんでした。これにより、Bulkcopy ラインに問題があるように見えました。

BulkCopy コードを 1 秒の遅延に置き換えたところ、ボトルネックにはなりませんでした。コンシューマー プロデューサーのキュー サイズが制御不能になっていることに気付いたのは、BulkCopy を 5 秒の遅延に置き換えたときだけでした。

この質問に時間を割いてくださったすべての人に感謝します。もっと面白い回答でなくてすみません。

于 2013-01-15T17:46:21.200 に答える
2

GC でリーダーを収集するには、リーダーを廃棄する必要がある場合があります。最終ブロックで、Dispose を呼び出そうとします。

finally
{
    //reader.Close();
    reader.Dispose();
}
于 2013-01-15T16:22:52.580 に答える