C# 增加堆大小 - 是否可能?

15
我使用C#读取大文件时出现内存不足异常。我需要修改代码,但暂时可以像在Java中一样增加堆大小(作为短期解决方案)吗?

1
你是一次性读取整个文件吗?还是可以分批读取和处理数据呢? - rerun
我相信现在.NET也有内存映射,http://blogs.msdn.com/b/salvapatuel/archive/2009/06/08/working-with-memory-mapped-files-in-net-4.aspx - Kelly Elton
5个回答

8

.Net会自动处理这个问题。

看起来你已经达到了一个.Net进程可以用于其对象的内存限制(在32位机器上,通过使用/3GB引导开关,标准为2GB或3GB。感谢Leppie和Eric Lippert提供的信息)。

重新考虑你的算法,或者考虑更换64位机器可能会有帮助。


1
32位系统的进程限制是2GB,除非您使用/3GB启动开关。 - leppie
我模糊地记得,32位系统上的.NET应用程序受到800MB或类似限制。 - OregonGhost
2
@OregonGhost:如果你分配了一个800MB的连续块(比如数组),你可能没有足够的虚拟空间了。 - leppie
6
让我们在这里准确地说明一下。一个进程可以拥有任意多的内存 -- 在32位Windows上,每个进程的限制足够大,以至于你在实践中永远不会用完它。所有这些内存中,只有2GB的内存可以同时映射到用户地址空间,如果启用了3GB开关,则可以映射3GB(3GB开关并不能给你4GB的地址空间;这个开关之所以称为“3GB开关”是有原因的)。由于.NET堆始终映射到用户地址空间,这限制了自动提供给托管程序的地址空间。 - Eric Lippert
@Eric Lippert,再次编辑。希望我的简单回答现在算是正确的。 - GvS
.NET 4.5现在只允许64位编译进程使用超过2GB的内存。 - Piotr Kula

5

不,这是不可能的。这个问题可能是由于您正在运行32位操作系统且内存过于分散所造成的。尝试不要将整个文件加载到内存中(例如逐行处理),或者当您确实需要完全加载它时,通过将其分成多个较小的部分来加载。


+1,因为在.NET中,碎片化是OutOfMemoryException的典型原因。 - OregonGhost
GvS 是正确的:.NET 会自动完成这个任务。手动实现是不可能的。 - Steven
这已经改变了,.NET 4.5中移除了限制 - 64位编译的进程可以使用超过2GB的内存。 - Piotr Kula

2

2
正如其他人已经指出的那样,这是不可能的。.NET运行时代表应用程序处理堆分配。
根据我的经验,当有足够的内存可用时(或者至少看起来是这样),.NET应用程序通常会遇到OOM问题。原因通常是使用了大型集合,例如数组、List(它使用数组来存储数据)或类似的东西。
问题在于,这些类型有时会在内存使用方面创建峰值。如果这些峰值请求无法被满足,则会抛出OOM异常。例如,当List需要增加其容量时,它会通过分配当前大小的两倍的新数组来实现,并将所有引用/值从一个数组复制到另一个数组。同样,像ToArray这样的操作也会创建一个新的数组副本。我还看到过在大型LINQ操作上出现类似的问题。
每个数组都作为连续的内存存储,因此为了避免OOM,运行时必须能够获得一个大块的内存。由于进程的地址空间可能由于DLL加载和堆的一般使用而被碎片化,因此这并不总是可能,在这种情况下,会抛出OOM异常。

0
你正在处理什么类型的文件?
如果是文本文件,最好使用StreamReader并yield返回ReadLine结果。
当然,你需要保留文件指针,但最坏情况大大降低。
对于二进制文件也有类似的方法。例如,如果你要上传文件到SQL,可以读取byte[]并使用Sql指针机制将缓冲区写入blob的末尾。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接