2010-09-29 77 views
5

在我們的應用程序中,我們正在使用System.IO.Packaging.Package類讀取XPS文件。當我們從PackagePart流中讀取數據時,我們可以從任務管理器中看到應用程序的內存消耗增加。但是,讀取完成後,內存消耗不會回落到從流中讀取數據之前的水平。從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的內部實現緩存了讀取的數據。

謝謝!

+0

我不知道爲什麼這個問題是downvoted。 – 2010-09-29 07:38:11

回答

0

您沒有指定如何測量應用程序的「內存消耗」,但也許您正在使用任務管理器?爲了更好地瞭解正在發生的事情,我建議您檢查應用程序的一些性能計數器。 .NET堆和一般進程內存性能計數器都可用。

如果你真的想了解你的應用程序如何使用內存的細節,你可以使用Microsoft CLR profiler

您看到的可能是.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可到達的任何託管內存(例如程序包緩衝區)都不會被垃圾回收。

+0

感謝您的回覆!我正在使用TaskManager。 yup會嘗試將CLR分析器作爲另一種選擇。我擔心的是浪費時間試圖找到一個解決方案,這個解決方案實際上是隻有Microsoft可以修復的內部PackagePart實現代碼中的一個錯誤。我還嘗試用FileStream替換PackagePart流,該FileStream將1MB文件的內容讀入數組。這與上面的代碼完成相同的次數。它基本上是相同的程序,但只能從不同的流中讀取。在這種情況下,內存正在被收集。它甚至沒有達到50MB。 – bjutus 2010-09-30 05:59:55

+0

嗯。我在調用ReadPackage()後立即調用GC.Collect(),但沒有發生任何事情。然而,我調用_package.Close(),然後在ReadPackage()之後調用GC.Collect(),並且內存使用量從100MB下降到大約20MB。那麼Package可能包含流的引用? – bjutus 2010-09-30 07:20:55

+0

在你的答案中看到了你的更新。再次感謝。我只嘗試GC.Collect()來檢查內存是否真的沒有被任何人幫上忙。看起來像是,直到包裹關閉。當我們不再需要它時,我們確實會在包裝上貼緊。問題在於它是我們應用程序中數據的主要來源,所以它必須在整個應用程序的整個生命週期中保持開放。 PackagePart流盡管是我們並不需要的東西。這就是爲什麼我們希望在我們離開ReadPackage()方法的範圍時釋放內存。也許該軟件包有某種內部緩存... – bjutus 2010-09-30 12:17:33