私はストリーミング twitter クライアントで作業しています。1 ~ 2 日間連続して実行した後、メモリ使用量が 1.4 ギガ (32 ビット プロセス) を超えており、その量に達するとすぐにメモリ不足になります。基本的にこれであるコードの例外 (このコードは、私のマシンでは 30 秒以内にエラーになります):
while (true)
{
Task.Factory.StartNew(() =>
{
dynamic dyn2 = new ExpandoObject();
//get a ton of text, make the string random
//enough to be be interned, for the most part
dyn2.text = Get500kOfText() + Get500kOfText() + DateTime.Now.ToString() +
DateTime.Now.Millisecond.ToString();
});
}
私はそれをプロファイリングしましたが、それは間違いなく DLR のクラスがかなり下にあるためです (メモリから - ここには詳細な情報はありません) xxRuntimeBinderxx と xxAggregatexx です。
Eric Lippert (microsoft) からのこの回答は、コード内の何も参照されていないにもかかわらず、GC が実行されない舞台裏でオブジェクトを解析する式を作成していることを示しているようです。
その場合、上記のコードにそれを防止または軽減する方法はありますか?
私のフォールバックは、動的な使用法を排除することですが、そうしないことを好みます。
ありがとう
アップデート:
12/14/12:
答え:
この特定の例でタスクを解放する方法は、yield (Thread.Sleep(0)) でした。これにより、解放されたタスクを GC で処理できるようになります。この特定のケースでは、メッセージ/イベント ループの処理が許可されていなかったと思います。
私が使用していた実際のコード(TPL Dataflow) では、ブロックでComplete()を呼び出していませんでした。これは、ブロックが終わりのないデータフローであることを意図していたためです。タスクは、Twitter が送信する限り Twitter メッセージを受け取ります。このモデルでは、アプリが実行されている限りブロックは決して完了しないため、ブロックのいずれかが完了したことを通知する理由はまったくありませんでした。
残念ながら、Dataflow ブロックは、送信されたすべてのものへの参照を実際に保持しているため、非常に長時間実行したり、膨大な数のアイテムを処理したりするように設計されていないようです。私が間違っている場合は、お知らせください。
したがって、回避策は定期的に(メモリ使用量に基づいて-私の場合は100kのTwitterメッセージごとでした)ブロックを解放し、再度設定することです.
このスキームでは、メモリ消費量が 80 メガを超えることはなく、ブロックをリサイクルして適切な測定のために GC を強制した後、gen2 ヒープは 6 メガに戻り、すべてが正常に戻ります。
10/17/12:
- 「これは何の役にも立たない」 : この例は、単に問題を迅速に生成できるようにするためのものです。問題とは関係のない数百行のコードから要約されています。
- 「タスクを作成し、次にオブジェクトを作成する無限ループ」: 覚えておいてください-これは問題を簡単に示しているだけです-実際のコードはそこに座って、さらにストリーミングデータを待っています. また、コードを見ると、すべてのオブジェクトがタスクの Action<> ラムダ内に作成されています。範囲外になった後、(最終的には) クリーンアップされないのはなぜですか? この問題は、実行が速すぎることによるものでもありません。実際のコードでは、メモリ不足の例外に到達するのに 1 日以上かかります。これにより、物事を試すのに十分な速さになります。
- 「タスクは解放されることが保証されていますか?」オブジェクトはオブジェクトですね。私の理解では、スケジューラはプール内のスレッドを使用しているだけであり、実行中のラムダは、実行が完了した後に破棄されます。