8

現在、FlowDocument リソースの解放に関する問題に対処しようとしています。rtf ファイルを読み込んで、TextRange.Load で FlowDocument に入れています。これを行った後、それらのリソースを保持し、GC はそれを収集しないことに気付きました。メモリ プロファイラを実行したところ、これが正しいことがわかりました。また、実際にロードして rtf を FlowDocument に入れるように絞り込みました。私がそれをしなければ、すべて大丈夫です。だから私はこれが問題であることを知っています。

この問題をどのように解決できるかについてのガイダンスを期待しています。これは、rtf とすべてをロードするコードです。他のすべてのコードをコメントアウトし、それを独自のスコープに入れ、GC.Collect() を試しました。どんな助けでも大歓迎です。

編集:これが現時点での私のコードです。動作させるために最低限必要なものを除いて、他のすべてを取り出しました。問題はまだあります。ご覧のとおり、FlowDocument と TextRange は他のどこにも参照されていません。

    public LoadRTFWindow(string file)
    {
        InitializeComponent();

        using (FileStream reader = new FileStream(file, FileMode.Open))
        {
            FlowDocument doc = new FlowDocument();
            TextRange range = new TextRange(doc.ContentStart, doc.ContentEnd);
            range.Load(reader, System.Windows.DataFormats.Rtf);
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }

この投稿を見つけました。問題の解決に役立つことを期待していましたが、うまくいきませんでした。どんな種類の助けも大歓迎です。ありがとうございました。

編集:これをチェックしている主な方法について言及する必要があると思います。Windows タスク マネージャーを開いて、アプリケーションのプロセスが使用しているメモリの使用状況を監視しています。上記のコードを実行すると、TextRange.Load() の実行中にアプリケーションが 40,000K から 70,000K になり (これは 400 ページの大きな RTF です)、終了すると 61,000K に落ちてそこにとどまります。私の予想では、40,000K まで低下するか、少なくともそれに非常に近い値になると予想しています。

前に述べたように、メモリ プロファイラを使用したところ、たくさんの段落、実行などがあることがわかりました。オブジェクトはその後も生きています。

4

8 に答える 8

6

メモリ リークがあることを確認した場合、問題をデバッグするために次のことを行います。

  1. http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx#aから Windows 用のデバッグ ツールをインストールします。
  2. インストール ディレクトリから Windbg を起動します。
  3. アプリケーションを起動し、メモリ リークを引き起こす操作を実行します。
  4. Windbg をアプリケーションにアタッチします (F6)。
  5. タイプ.loadby sos mscorwks
  6. タイプ!dumpheap -type FlowDocument
  7. 上記のコマンドの結果を確認します。複数の FlowDocument が表示される場合は、最初の列 (アドレスを含む) の各値に対して、次のようにします。

タイプ!gcroot <value of first column>

これにより、参照を保持しているユーザーが表示されます。

于 2009-06-23T04:26:01.547 に答える
3

別のスレッドでフロー ドキュメントを作成するという同様の問題がありました。メモリ プロファイラーで、オブジェクトがまだそこにあることに気付きました。

私の知る限り、このリンクで説明されているように

"FlowDocument が作成されると、その StructuralCache に比較的高価な書式設定コンテキスト オブジェクトも作成されます。タイトなループで複数の FlowDoc を作成すると、FlowDoc ごとに StructuralCache が作成されます。最後に Gc.Collect を呼び出してみましょう。一部のメモリを回復することを期待してループします. StructuralCache にはファイナライザがこの書式設定コンテキストを解放しますが、すぐには解放されません. ファイナライザは DispatcherPriority.Background でコンテキストを解放する操作を効果的にスケジュールします."

そのため、ディスパッチャの操作が完了するまで、フロー ドキュメントはメモリ内にあります。したがって、ディスパッチャー操作を完了するという考えです。

Dispatcher が現在実行されているスレッドにいる場合は、以下のコードを試してください。SystemIdle の優先度が最も低いため、キュー内のすべての操作が強制的に完了されます。

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.SystemIdle, 
    new DispatcherOperationCallback(delegate { return null; }), null); 

Dispatcher が実行されていないスレッドにいる場合、私の場合はスレッドで単一のフロー ドキュメントのみが作成されたので、次のようなことを試しました。

var dispatcher = Dispatcher.CurrentDispatcher;
dispatcher.BeginInvokeShutdown(DispatcherPriority.SystemIdle);
Dispatcher.Run();

これにより、最後にシャットダウンがキューに入れられ、ディスパッチャーが実行されて FlowDocument が消去され、最後にディスパッチャーがシャットダウンされます。

于 2010-11-12T15:34:44.483 に答える
1

FlowDocument は System.Windows.Threading.Dispatcher を使用してすべてのリソースを解放します。ファイナライザーはすべてのリソースが解放されるまで現在のスレッドをブロックするため、ファイナライザーは使用しません。そのため、UI がフリーズするなどの現象が発生する場合があります。ディスパッチャーはバックグラウンド スレッドで実行され、UI への影響が少なくなります。
したがって、GC.WaitForPendingFinalizers(); を呼び出します。これには影響しません。待機するコードを追加して、ディスパッチャが作業を完了できるようにするだけです。Thread.CurrentThread.Sleep(2000 /* 2 secs */); のようなものを追加してみてください。

編集:アプリケーションのデバッグ中にこの問題を発見したようです。次の非常に単純なテスト ケース (コンソール プログラム) を作成しました。

    static void Main(string[] args)
    {
        Console.WriteLine("press enter to start");
        Console.ReadLine();

        var path = "c:\\1.rtf";

        for (var i = 0; i < 20; i++)
        {
            using (var stream = new FileStream(path, FileMode.Open))
            {
                var document = new FlowDocument();
                var range = new TextRange(document.ContentStart, document.ContentEnd);

                range.Load(stream, DataFormats.Rtf);
            }
        }

        Console.WriteLine("press enter to run GC.");
        Console.ReadLine();

        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("GC has finished .");
        Console.ReadLine();
    }

問題を再現しようとしました。私はそれを数回実行しましたが、完全に機能していました-リークはありませんでした(常に約3,2Kb、36ハンドル)。このプログラムをVSでデバッグモードで実行するまで再現できませんでした(ctrl + f5の代わりにf5のみ)。最初に 20,5Kb、ロード後と GC 前に 31,7Kb、GC 後に 31Kb を受け取りました。それはあなたの結果に似ています。
では、VS ではなくリリース モードで実行しているこの問題を再現していただけますか?

于 2009-06-05T00:09:15.007 に答える
1

私は以前に#7をやっていました。Windbg を使用するのは初めてだったので、参照を見つけるためにアドレスをどうすればよいかわかりませんでした。これが私が得たものです。

 Address       MT     Size
0131c9c0 55cd21d8       84     
013479e0 55cd21d8       84     
044dabe0 55cd21d8       84     
total 3 objects
Statistics:
      MT    Count    TotalSize Class Name
55cd21d8        3          252 System.Windows.Documents.FlowDocument
Total 3 objects
0:011> !gcroot 0131c9c0
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 47c
Scan Thread 2 OSTHread be8
Scan Thread 4 OSTHread 498
DOMAIN(001657B0):HANDLE(WeakSh):911788:Root:0131ff98(System.EventHandler)->
0131fcd4(System.Windows.Documents.AdornerLayer)->
012fad68(MemoryTesting.Window2)->
0131c9c0(System.Windows.Documents.FlowDocument)
DOMAIN(001657B0):HANDLE(WeakSh):911cb0:Root:0131ca90(MS.Internal.PtsHost.PtsContext)->
0131cb14(MS.Internal.PtsHost.PtsContext+HandleIndex[])->
0133d668(MS.Internal.PtsHost.TextParagraph)->
0131c9c0(System.Windows.Documents.FlowDocument)
DOMAIN(001657B0):HANDLE(WeakSh):9124a8:Root:01320a2c(MS.Internal.PtsHost.FlowDocumentPage)->
0133d5d0(System.Windows.Documents.TextPointer)->
0131ca14(System.Windows.Documents.TextContainer)->
0131c9c0(System.Windows.Documents.FlowDocument)

(読みやすいようにコードブロックに入れました)。窓を閉めた後です。そのため、いくつかのことから参照されているようです。これがわかったので、これらの参照を解放して FlowDocument を解放できるようにするにはどうすればよいでしょうか。

ご協力いただきありがとうございます。やっと地盤を固めた感じです。

于 2009-06-24T03:04:01.347 に答える
0

GC.Collect()それ自体ではすべてを収集するわけではありません。実行する必要があります。

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

また、ランタイムが収集されたメモリをすぐに解放するとは限らないことがわかりました。タスクマネージャに依存するのではなく、実際のヒープサイズを確認する必要があります。

于 2009-06-14T04:37:29.677 に答える
0

FlowDocument の親がぶらぶらしていないことを確認してください。こちらを参照してください。「FlowDocument をインスタンス化すると、コンテンツをホストする親 FlowDocumentPageViewer が自動的に生成されます。」そのコントロールがぶら下がっている場合、それが問題の原因である可能性があります。

于 2009-06-04T21:01:58.333 に答える
0

そのファイル ハンドルを解放することを検討してください。また、IDisposable.Dispose を呼び出す代わりに、"using" ステートメントを使用することも検討してください (しゃれは意図されていません)。

于 2009-06-14T20:50:08.263 に答える
0

問題を再現しようとしましたが、私のマシンでは発生しません。

タスク マネージャーはワーキング セットのサイズを表示しますが、これはプログラムのメモリ使用量を正確に表すものではありません。代わりに perfmon を使用してみてください。

  1. スタート -> ファイル名を指定して実行 -> perfmon.msc
  2. アプリケーションのすべてのヒープに .NET CLR Memory/#Bytes を追加します

実験を繰り返して、そのカウンターが上がり続けるかどうかを確認します。そうでない場合は、マネージ メモリをリークしていないことを意味します。

于 2009-06-22T19:14:30.747 に答える