从 PackagePart 流读取不会释放内存
在我们的应用程序中,我们使用 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的内部实现是缓存读取的数据。
谢谢你!
In our application, we are reading an XPS file using the System.IO.Packaging.Package class. When we read from a stream of a PackagePart, we can see from the Task Manager that the application's memory consumption rises. However, when the reading is done, the memory consumption doesn't fall back to what it was before reading from the stream.
To illustrate the problem, I wrote a simple code sample that you can use in a stand alone wpf application.
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();
}
}
The ReadPackage() method will read all the PackagePart objects' stream contents into a local array. In the sample, I used a 1000 page XPS document as the package source in order to easily see the memory consumption change of the application. On my machine, the stand alone app's memory consumption starts at 18MB then rises to 100MB after calling the method. Calling the method again can raise the memory consumption again but it can fall back to 100MB. However, it doesn't fall back to 18MB anymore.
Has anyone experienced this while using PackagePart? Or am I using it wrong? I think the internal implementation of PackagePart is caching the data that was read.
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您没有指定如何测量应用程序的“内存消耗”,但也许您正在使用任务管理器?为了更好地了解正在发生的情况,我建议您检查应用程序的一些性能计数器。 .NET 堆和通用进程内存性能计数器均可用。
如果您确实想了解应用程序如何使用内存的详细信息,您可以使用 Microsoft CLR 分析器。
您所看到的可能是 .NET 堆扩展以容纳非常大的文件的结果。大对象被放置在大对象堆 (LOH) 上,即使 .NET 内存被垃圾回收,空闲内存也永远不会返回给操作系统。此外,LOH 上的对象在垃圾收集期间永远不会移动,这可能会导致 LOH 碎片耗尽可用地址空间,即使有大量可用内存也是如此。
如果您想控制包使用的资源,那么您就没有以最佳方式使用它。包是一次性的,通常您应该像这样使用它:
在
using
语句末尾,包消耗的资源要么已经释放,要么可以被垃圾收集。如果您确实想保留表单的
_package
成员,您应该在某个时候调用Close()
(或IDisposable.Dispose()
)来释放资源。不建议调用GC.Collect()
,并且不一定能够回收包使用的资源。无论您多久尝试强制一次垃圾回收,任何可从_package
访问的托管内存(例如包缓冲区)都不会被垃圾回收。You do not specify how you measure the "memory consumption" of your application but perhaps you are using task manager? To get a better view of what is going on I suggest that you examine some performance counters for your application. Both .NET heap and general process memory performance counters are available.
If you really want to understand the details of how your application uses memory you can use the Microsoft CLR profiler.
What you see may be a result of the .NET heap expanding to accomodate a very large file. Big objects are placed on the Large Object Heap (LOH) and even if the .NET memory is garbage collected the free memory is never returned to the operating system. Also, objects on the LOH are never moved around during garbage collection and this may fragment the LOH exhausting the available address space even though there is plenty of free memory.
If you want to control the resources used by the package you are not using it in the best way. Packages are disposable and in general you should use it like this:
At the end of the
using
statement resources consumed by the package are either already released or can be garbage collected.If you really want to keep the
_package
member of your form you should at some point callClose()
(orIDisposable.Dispose()
) to release the resources. CallingGC.Collect()
is not recommended and will not necessarily be able to recycle the resources used by the package. Any managed memory (e.g. package buffers) that are reachable from_package
will not be garbage collected no matter how often you try to force a garbage collection.