5

このアプリケーションでは、System.IO.Packaging.Package クラスを使用して XPS ファイルを読み取ります。PackagePart のストリームから読み取ると、アプリケーションのメモリ消費量が増加していることがタスク マネージャーからわかります。ただし、読み取りが完了すると、メモリ消費量はストリームから読み取る前の状態には戻りません。

この問題を説明するために、スタンドアロンの wpf アプリケーションで使用できる簡単なコード サンプルを作成しました。

 public partial class Window1 : Window
 {
        public Window1()
        {
            InitializeComponent();

            _package = Package.Open(@"c:\test\1000pages.xps", FileMode.Open, FileAccess.ReadWrite, FileShare.None);

        }

        private void ReadPackage()
        {
            foreach (PackagePart part in _package.GetParts())
            {
                using (Stream partStream = part.GetStream())
                {
                    byte[] arr = new byte[partStream.Length];
                    partStream.Read(arr, 0, (int)partStream.Length);
                    partStream.Close();
                }
            }
        }

        Package _package;
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ReadPackage();      
        }
 }

ReadPackage() メソッドは、すべての PackagePart オブジェクトのストリーム コンテンツをローカル配列に読み取ります。サンプルでは、​​アプリケーションのメモリ消費量の変化を簡単に確認するために、パッケージ ソースとして 1000 ページの XPS ドキュメントを使用しました。私のマシンでは、スタンドアロン アプリのメモリ消費量は 18MB から始まり、メソッドを呼び出すと 100MB に増加します。メソッドを再度呼び出すと、メモリ消費量が再び増加する可能性がありますが、100MB に戻る可能性があります。ただし、もう 18MB には戻りません。

PackagePart の使用中にこれを経験した人はいますか? それとも私の使い方が悪いのでしょうか?PackagePart の内部実装は、読み取ったデータをキャッシュしていると思います。

ありがとうございました!

4

1 に答える 1

0

アプリケーションの「メモリ消費量」を測定する方法を指定していませんが、タスクマネージャーを使用している可能性がありますか?何が起こっているのかをよりよく理解するために、アプリケーションのパフォーマンスカウンターを調べることをお勧めします。.NETヒープと一般的なプロセスメモリパフォーマンスカウンターの両方が使用可能です。

アプリケーションがメモリを使用する方法の詳細を本当に理解したい場合は、MicrosoftCLRプロファイラーを使用できます。

表示されるのは、非常に大きなファイルに対応するために.NETヒープが拡張された結果である可能性があります。ビッグオブジェクトはラージオブジェクトヒープ(LOH)に配置され、.NETメモリがガベージコレクションされた場合でも、空きメモリがオペレーティングシステムに戻されることはありません。また、LOH上のオブジェクトは、ガベージコレクション中に移動されることはありません。これにより、空きメモリが十分にある場合でも、LOHが断片化されて使用可能なアドレス空間が使い果たされる可能性があります。

PackagePartを使用しているときにこれを経験した人はいますか?それとも私はそれを間違って使用していますか?

パッケージによって使用されるリソースを制御したい場合は、それを最善の方法で使用していません。パッケージは使い捨てであり、一般的には次のように使用する必要があります。

using (var package = Package.Open(@"c:\test\1000pages.xps", FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
  // ... process the package
}

ステートメントの最後でusing、パッケージによって消費されたリソースはすでに解放されているか、ガベージコレクションできます。

フォームのメンバーを本当に保持したい場合_packageは、ある時点でClose()(またはIDisposable.Dispose())を呼び出してリソースを解放する必要があります。呼び出すGC.Collect()ことはお勧めできません。また、パッケージで使用されているリソースを必ずしもリサイクルできるとは限りません。ガベージコレクションを強制しようとする頻度に関係なく、そこから到達可能な管理対象メモリ(パッケージバッファなど)は_packageガベージコレクションされません。

于 2010-09-29T07:53:34.607 に答える