当仍有大量内存空闲时,会抛出“System.OutOfMemoryException”

这是我的代码:

int size = 100000000; double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb double[] randomNumbers = new double[size]; 

exception:types“System.OutOfMemoryException”的exception被抛出。

我在这台机器上有4GB内存当我启动这个运行时, 2.5GB是免费的 ,在PC上有足够的空间来处理100000000个随机数的762mb。 在可用内存的情况下,我需要尽可能多地存储随机数。 当我投入生产时,盒子上会有12GB,我想利用它。

CLR是否将我限制为默认的最大内存? 我如何要求更多?

更新

我认为把这个分成更小的块,并增加我的内存需求将有助于,如果这个问题是由于内存碎片 ,但它不是我不能超过256MB总ArrayList的大小,无论我调整blockSize

 private static IRandomGenerator rnd = new MersenneTwister(); private static IDistribution dist = new DiscreteNormalDistribution(1048576); private static List<double> ndRandomNumbers = new List<double>(); private static void AddNDRandomNumbers(int numberOfRandomNumbers) { for (int i = 0; i < numberOfRandomNumbers; i++) { ndRandomNumbers.Add(dist.ICDF(rnd.nextUniform())); } } 

从我的主要方法:

 int blockSize = 1000000; while (true) { try { AddNDRandomNumbers(blockSize); } catch (System.OutOfMemoryException ex) { break; } } double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0; 

您可能想阅读:“ ”内存不足“不参考物理内存 ”由Eric Lippert。

总之,非常简单,“内存不足”并不意味着可用内存的数量太小。 最常见的原因是,在当前地址空间内,没有足够大的内存连续部分来服务所需的分配。 如果您有100个块,每个4 MB大,当您需要一个5 MB块时,这不会帮助您。

关键点:

  • 我们称之为“进程内存”的数据存储在我看来是最好的可视化的磁盘上的一个海量文件
  • RAM可以被看作仅仅是性能优化
  • 您的程序消耗的虚拟内存总量实际上与性能无关
  • “用完RAM”很less会导致“内存不足”错误。 而不是一个错误,它会导致性能不佳,因为存储实际上在磁盘上的全部成本突然变得相关。

为了分配762MB,你没有连续的内存块,你的内存被分割,分配器找不到足够大的空间来分配所需的内存。

  1. 您可以尝试使用/ 3GB(如其他人所build议的)
  2. 或切换到64位操作系统。
  3. 或者修改algorithm,这样就不需要很大的内存。 也许分配一些较小(相对)的内存块。

检查您正在构build一个64位进程,而不是32位,这是Visual Studio的默认编译模式。 要做到这一点,右键单击您的项目,属性 – >生成 – >平台目标:x64。 与任何32位进程一样,32位编译的Visual Studio应用程序的虚拟内存限制为2GB。

64位进程没有这个限制,因为他们使用64位指针,所以他们的理论最大地址空间(他们的虚拟内存的大小)是16艾字节(2 ^ 64)。 实际上,Windows x64将进程的虚拟内存限制为8TB。 内存限制问题的解决scheme是在64位编译。

但是,默认情况下,Visual Studio中的对象大小仍然限制为2GB。 您将能够创build几个组合大小大于2GB的数组,但是您不能默认创build大于2GB的数组。 希望如果你仍然想创build大于2GB的数组,你可以通过在你的app.config文件中添加如下代码来实现:

 <configuration> <runtime> <gcAllowVeryLargeObjects enabled="true" /> </runtime> </configuration> 

正如你可能知道的那样,问题是你正在尝试分配一个大的连续的内存块,由于内存碎片而无法工作。 如果我需要做你在做什么,我会做以下几点:

 int sizeA = 10000, sizeB = 10000; double sizeInMegabytes = (sizeA * sizeB * 8.0) / 1024.0 / 1024.0; //762 mb double[][] randomNumbers = new double[sizeA][]; for (int i = 0; i < randomNumbers.Length; i++) { randomNumbers[i] = new double[sizeB]; } 

然后,要得到一个特定的索引,你可以使用randomNumbers[i / sizeB][i % sizeB]

如果您总是按顺序访问值,另一个选项可能是使用重载的构造函数来指定种子。 这样你会得到一个半随机数(如DateTime.Now.Ticks )将其存储在一个variables中,然后当你开始浏览列表时,你将使用原始种子创build一个新的Random实例:

 private static int randSeed = (int)DateTime.Now.Ticks; //Must stay the same unless you want to get different random numbers. private static Random GetNewRandomIterator() { return new Random(randSeed); } 

值得注意的是,尽pipe弗雷德里克·莫克(FredrikMörk)答案中的博客指出,这个问题通常是由于缺less地址空间 ,所以没有列出其他许多问题,如2GB CLR对象大小限制(在评论中提到ShuggyCoUk在同一个博客上),掩盖了内存碎片,并没有提到页面文件大小的影响(以及如何使用CreateFileMapping函数来解决这个问题)。

2GB的限制意味着randomNumbers必须小于2GB。 由于数组是类,并有自己的开销,这意味着一个double数组将需要更小,然后2 ^ 31。 我不确定这个长度是多less,那么长度是多less,但是.NET数组是多less? 表示12 – 16字节。

内存碎片与硬盘碎片非常相似。 你可能有2GB的地址空间,但是当你创build和销毁对象时,这些值之间会有差距。 如果这些间隙对于大对象来说太小,并且不能请求额外的空间,那么您将得到System.OutOfMemoryException 。 例如,如果您创build200万个1024字节的对象,那么您使用的是1.9GB。 如果删除地址不是3的倍数的每个对象,那么您将使用.6GB的内存,但是它将通过2024字节的开放块分布在地址空间中。 如果你需要创build一个2GB的对象,你将无法做到这一点,因为没有足够大的块来适应它,并且无法获得额外的空间(假设32位环境)。 这个问题的可能的解决scheme就像使用更小的对象,减less存储在内存中的数据量,或者使用内存pipe理algorithm来限制/防止内存碎片。 应该指出的是,除非你正在开发一个使用大量内存的大型程序,否则这将不是问题。 此外,这个问题可能出现在64位系统上,因为窗口主要受限于页面文件大小和系统上的RAM数量。

由于大多数程序从操作系统请求工作内存,不要求文件映射,它们将受到系统的RAM和页面文件大小的限制。 正如NéstorSánchez(NéstorSánchez)在博客上的评论所指出的那样,像C#这样的托pipe代码,您坚持了RAM /页面文件的限制和操作系统的地址空间。


那是预期的那么长。 希望它可以帮助别人。 我发布它,因为我碰到System.OutOfMemoryException运行系统上有24GB内存的x64程序,即使我的数组只有2GB的东西。

我build议对/ 3GB的Windows启动选项。 除了其他的东西(对于一个性能不好的应用程序来说这样做太矫枉过正了,它可能无法解决你的问题),它会导致很多的不稳定。

许多Windows驱动程序没有使用此选项进行testing,因此其中不less用户模式指针总是指向地址空间的较低2GB。 这意味着它们可能会以/ 3GB的速度崩溃。

但是,Windows通常会将32位进程限制为2GB的地址空间。 但这并不意味着你应该能够分配2GB!

地址空间已经充斥着各种分配的数据。 有堆栈,加载的所有程序集,静态variables等等。 不能保证任何地方都会有800MB的连续未分配内存。

分配2个400MB块可能会更好。 或者4个200MB块。 在分散的存储空间中,较小的分配更容易find空间。

无论如何,如果你打算把它部署到一个12GB的机器上,你会想把它作为一个64位的应用程序来运行,这应该可以解决所有的问题。

从32位更改为64位为我工作 – 值得一试,如果你在一个64位电脑上,它不需要端口。

如果你需要这样大的结构,也许你可以利用内存映射文件。 本文可能会有帮助: http : //www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

LP,Dejan

32位窗口有2GB的进程内存限制。 其他人提到的/ 3GB引导选项将会使这个3GB剩余1GB的内核用于操作系统内核。 实际上,如果你想使用超过2GB没有麻烦,那么一个64位操作系统是必需的。 这也克服了这个问题,虽然你可能有4GB的物理内存,但是对于video卡需要的地址空间可以使内存的大小不能用 – 通常大约为500MB。

而不是分配一个巨大的数组,你可以尝试使用迭代器吗? 这些是延迟执行的,意味着只有在foreach语句中请求时才会生成值。 你不应该用这样的方式耗尽内存:

 private static IEnumerable<double> MakeRandomNumbers(int numberOfRandomNumbers) { for (int i = 0; i < numberOfRandomNumbers; i++) { yield return randomGenerator.GetAnotherRandomNumber(); } } ... // Hooray, we won't run out of memory! foreach(var number in MakeRandomNumbers(int.MaxValue)) { Console.WriteLine(number); } 

上面的代码会根据你的意愿产生尽可能多的随机数,但是只有通过foreach语句才能生成。 你不会用这种方式耗尽内存。

或者,如果您必须将它们全部放在一个地方,请将它们存储在文件中而不是存储器中。

那么,我遇到了一个大数据集的类似问题,并试图强制应用程序使用这么多的数据是不正确的select。 我可以给你的最好的提示是如果可能的话,用小块处理你的数据。 因为处理这么多的数据,问题迟早会回来的。 另外,你无法知道每个将运行应用程序的机器的configuration,所以总是有可能在另一台pc上发生exception。

我有一个类似的问题,这是由于一个StringBuilder.ToString();

将您的解决scheme转换为x64。 如果你仍然面临一个问题,给所有引发如下exception的所有内容赋予最大长度:

  var jsSerializer = new JavaScriptSerializer(); jsSerializer.MaxJsonLength = Int32.MaxValue; 

将Windows进程限制增加到3GB。 (通过boot.ini或Vista启动pipe理器)