从PackagePart流中读取不会释放内存

时间:2010-09-29 07:27:54

标签: c# memory stream package xps

在我们的应用程序中,我们使用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的内部实现是缓存已读取的数据。

谢谢!

1 个答案:

答案 0 :(得分: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访问的托管内存(例如包缓冲区)都不会被垃圾回收。