我试图向Collection中添加大量元素,每个简单数据传输对象的元素具有基本数据类型的五个属性,没什么特别之处.
在循环中添加新条目时,我总是得到一个OutOfMemoryException.有趣的是,在尝试添加8388608th元素(8*1024*1024)时,我总是得到异常.因此,我假设此类集合中允许的容量(元素数量)存在内置限制,但我找不到任何有关它的信息.
这个限制确实存在吗?我会在哪里找到这个记录?
Joe*_*orn 15
这是一个OutOfMemoryException,因此这里的问题不是集合的大小或容量:它是应用程序中的内存使用.诀窍在于您不必耗尽机器中的内存,甚至不需要在您的过程中使用内存来获取此异常.
我认为正在发生的是你正在填满大型对象堆.随着集合的增长,他们需要在后台添加存储以容纳新项目.分配新存储并复制项目后,旧存储将被释放,并且应符合垃圾回收的条件.
问题是,一旦超出一定的大小(过去是85000字节,但现在可能不同),垃圾收集器(GC)使用称为大对象堆(LOH)的东西来跟踪你的内存.当GC从LOH中释放内存(这种情况很少开始时),内存将返回到您的操作系统并可用于其他进程,但该内存中的虚拟地址空间仍将在您自己的进程中使用.你的程序的地址表中有一个很大的漏洞,因为这个漏洞位于大对象堆上,它永远不会被压缩或回收.
您在2的精确幂上看到此异常的原因是大多数.Net集合使用加倍算法来向集合添加存储.它总是会抛出需要再次加倍的点,因为直到那时RAM已经被分配了.
因此,快速解决方案是利用大多数.Net集合的一个很少使用的功能.如果查看构造函数重载,大多数集合类型都会有一个允许您在初始构造期间设置容量的类型.这个容量并不是一个硬限制 - 它只是一个起点 - 但它在少数情况下很有用,包括当你的集合会变得非常大时.你可以将初始容量设置为淫秽的东西...希望有足够大的东西来容纳你所有的物品,或者至少只需要"加倍"一次或两次.
您可以通过在控制台应用程序中运行以下代码来查看此效果:
var x = new List<int>();
for (long y = 0; y < long.MaxValue; y++)
x.Add(0);
Run Code Online (Sandbox Code Playgroud)
在我的系统上,在134217728项之后抛出OutOfMemory异常.134217728*每个int 4个字节只有(并且确切地说)512MB的RAM.它不应该抛出,因为这是过程中任何实际大小的唯一东西,但它无论如何都会因为旧版本的集合丢失了地址空间.
现在让我们改变代码来设置容量,如下所示:
var x = new List<int>(134217728 * 2);
for (long y = 0; y < long.MaxValue; y++)
x.Add(0);
Run Code Online (Sandbox Code Playgroud)
现在我的系统在它抛出时一直到268435456个项目(1GB的RAM),这样做,因为它不能加倍1GB,这要归功于进程使用的其他ram吃2GB虚拟地址表限制的部分(即:循环计数器和集合对象和进程本身的任何开销).
我无法解释的是,它不允许我使用3作为乘数,即使那只是(!)1.5GB.一个小实验使用不同的乘数试图找出我能得到多大,表明数字不一致.有一次我能够超过2.6,但后来不得不回到2.4以下.我想是一些新发现的东西.
如果此解决方案确实为您提供了足够的空间,那么您还可以使用一个技巧来获得3GB的虚拟地址空间,或者您可以强制您的应用程序编译为x64而不是x86或AnyCPU.如果您使用的是基于2.0运行时的框架版本(通过.Net 3.5提供的任何内容),您可以尝试更新到.Net 4.0或更高版本,据说这有点好一点.如果失败了,你将不得不重新编写一份关于如何处理数据的完整重写,这可能涉及将数据保存在磁盘上,并且一次只能在内存中保存一个项目或一小部分项目(缓存).我真的推荐这个最后一个选项,因为其他任何东西都可能最终意外地再次破坏(如果你的数据集开始时这么大,它也可能会增长).
| 归档时间: |
|
| 查看次数: |
3146 次 |
| 最近记录: |