1

さらに別のメモリを解放する方法の質問:

現在同一である2つのデータベース間でデータをコピーしていますが、間もなく同期がとれなくなります。これを行うReflectionとADO.Netエンティティを使用してC#でスケルトンアプリをまとめました。

ソースデータベースのテーブルごとに:

  • 宛先データベースの対応するテーブルをクリアします
  • ソーステーブルのオブジェクトごと
    • ソースオブジェクトのプロパティごとに
      • 同じ名前のプロパティが宛先オブジェクトに存在する場合は、Reflectionを使用してソースプロパティを宛先プロパティにコピーします

これは、ユーザーがアップロードしたファイルが含まれている大きな900MBのテーブルに到達するまではうまく機能します。

ブロブ(それぞれ最大7 MB)を自分のマシンにコピーして宛先データベースに戻すプロセスは、ローカルメモリを使い果たします。ただし、そのメモリは解放されておらず、約750 MB相当のデータがコピーされると、プロセスは停止します。OutOfMemoryExceptionがスローされたときにプログラムに1500 MBの割り当てられたスペースがあり、おそらくこれまでにコピーされたすべてのコピーが2つあります。

私は最初に単純なコピーをして、素朴なアプローチを試みました。私が大きなテーブルにたどり着くまで、それはすべてのテーブルで機能しました。GC.Collect()結果に明らかな変化を与えることなく、強制的にaを試してみました。また、スコープ外の参照がGCの取得に役立つことを期待して、実際のコピーを別の関数に入れてみました。Thread.Sleep私は、バックグラウンドプロセスを実行するためのより多くの時間を与えることを試みるためにさえ入れました。これらはすべて効果がありません。

現在存在する関連コードは次のとおりです。

public static void CopyFrom<TSource, TDest>(this ObjectSet<TDest> Dest, ObjectSet<TSource> Source, bool SaveChanges, ObjectContext context)
    where TSource : class
    where TDest : class {

    int total = Source.Count();
    int count = 0;
    foreach (var src in Source) {
        count++;
        CopyObject(src, Dest);

        if (SaveChanges && context != null) {
            context.SaveChanges();
            GC.Collect();
            if (count % 100 == 0) {
                Thread.Sleep(2000);
            }
        }
    }
}

CopyObject()関数は含めませんでした。リフレクションを使用して、srcのプロパティを評価し、Destに追加する新しいオブジェクトの同じ名前のプロパティに配置します。

SaveChangesは、この追加の処理を実行する必要があることを示すブール変数です。これは、大きなテーブルでのみtrueであり、それ以外の場合はfalseです。

だから、私の質問:メモリが不足しないようにこのコードを変更するにはどうすればよいですか?

4

1 に答える 1

4

問題は、データベースコンテキストが内部で多くのキャッシュを利用し、多くの情報を保持し、ガベージコレクターが(呼び出すかどうかに関係Collectなく)それを解放できないことです。

これは、コンテキストがスコープの高すぎる範囲で定義されていることを意味します。(編集に基づいて、テーブル全体で使用しているように見えます。それは...良くありません。)定義されている場所は示されていませんが、どこにある場合でも、おそらく下位レベルにあるはずです。接続プールがあるため、新しいコンテキストを作成するのは費用がかからず、ユースケースに基づいて、キャッシュされた情報の多くに依存する必要はないことに注意してください(アイテムに複数回触れていないため)。新しいコンテキストは、メモリフットプリントを大幅に削減しますが、パフォーマンスコストを追加するべきではありません。

于 2012-11-08T15:56:42.710 に答える