アプリケーションの 1 つで見られる問題を、信じられないほど単純な再現サンプルに要約しました。何かが間違っているか、何かが欠けているかどうかを知る必要があります。
とにかく、以下はコードです。この動作は、コードが実行され、OutOfMemoryException でクラッシュするまでメモリ内で着実に増加するというものです。これにはしばらく時間がかかりますが、動作としては、オブジェクトが割り当てられ、ガベージ コレクションは行われません。
私はメモリ ダンプを取得し、!gcroot を実行したり、ANTS を使用して問題を特定したりしましたが、しばらく作業を続けており、新しい目が必要です。
この再現サンプルは、Canvas を作成し、それに Line を追加する単純なコンソール アプリケーションです。これは継続的に行われます。これがコードのすべてです。システムが応答しなくなるほど CPU に負担がかからないようにするため (そして、GC が実行できないという奇妙な事態が発生しないようにするため)、時々スリープします。
誰にも考えはありますか?.NET 3.0 のみ、.NET 3.5、および .NET 3.5 SP1 でこれを試しましたが、3 つの環境すべてで同じ動作が発生しました。
また、このコードを WPF アプリケーション プロジェクトにも配置し、ボタン クリックでコードをトリガーしたことにも注意してください。
システムを使用する; System.Collections.Generic の使用; System.Linq を使用します。 System.Text を使用します。 System.Windows.Controls を使用します。 System.Windows.Shapes を使用します。 System.Windows を使用します。 名前空間 SimplestReproSample { クラス プログラム { [スタスレッド] static void Main(string[] args) { 長いカウント = 0; while (真) { もし (count++ % 100 == 0) { // CPU 全体を使い果たしていないことを確認するために、しばらくスリープします System.Threading.Thread.Sleep(50); } BuildCanvas(); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void BuildCanvas() { Canvas c = new Canvas(); 行 line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; 線幅 = 100; c.Children.Add(行); c.Measure(新しいサイズ(300, 300)); c.Arrange(new Rect(0, 0, 300, 300)); } } }
注: 以下の最初の回答は、WPF アプリケーションのボタン クリック イベント中に同じ動作が発生することを既に明示的に述べているため、少し根拠がありません。ただし、そのアプリでは限られた回数 (たとえば 1000 回) の反復のみを行うことを明示的に述べていませんでした。そのようにすると、アプリケーションをクリックしたときに GC を実行できます。また、メモリ ダンプを取得したところ、オブジェクトが !gcroot を介してルート化されていることがわかったと明示的に述べたことにも注意してください。また、GC を実行できないことにも同意しません。コンソール アプリケーションのメイン スレッドで GC が実行されません。特に、コンカレント ワークステーション GC がアクティブであることを意味するデュアル コア マシンを使用しているためです。ただし、メッセージ ポンプはあります。
その点を証明するために、DispatcherTimer でテストを実行する WPF アプリケーション バージョンを次に示します。100 ミリ秒のタイマー間隔中に 1000 回の反復を実行します。ポンプからのメッセージを処理し、CPU 使用率を低く保つのに十分な時間。
システムを使用する; System.Collections.Generic の使用; System.Linq を使用します。 System.Text を使用します。 System.Windows を使用します。 System.Windows.Controls を使用します。 System.Windows.Shapes を使用します。 名前空間 SimpleReproSampleWpfApp { パブリック部分クラス Window1 : ウィンドウ { プライベート System.Windows.Threading.DispatcherTimer _timer; public Window1() { InitializeComponent(); _timer = 新しい System.Windows.Threading.DispatcherTimer(); _timer.Interval = TimeSpan.FromMilliseconds(100); _timer.Tick += new EventHandler(_timer_Tick); _timer.Start(); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] void RunTest() { for (int i = 0; i < 1000; i++) { BuildCanvas(); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] private static void BuildCanvas() { Canvas c = new Canvas(); 行 line = new Line(); line.X1 = 1; line.Y1 = 1; line.X2 = 100; line.Y2 = 100; 線幅 = 100; c.Children.Add(行); c.Measure(新しいサイズ(300, 300)); c.Arrange(new Rect(0, 0, 300, 300)); } void _timer_Tick(オブジェクト送信者, EventArgs e) { _timer.Stop(); RunTest(); _timer.Start(); } } }
注2:最初の回答のコードを使用しましたが、メモリの成長が非常に遅かったです。1ms は私の例よりもはるかに遅く、反復回数が少ないことに注意してください。成長に気づき始める前に、数分間実行する必要があります。5 分後、30MB の開始点から 46MB になります。
注 3: .Arrange の呼び出しを削除すると、成長が完全になくなります。残念ながら、多くの場合、(RenderTargetBitmap クラスを介して) Canvas から PNG ファイルを作成しているため、この呼び出しは私の使用にとって非常に重要です。.Arrange を呼び出さないと、キャンバスはまったくレイアウトされません。