当仍然有大量空闲内存时,抛出“ System. OutOfMemory yException”

这是我的暗号:

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

例外: 抛出了类型为“ System.OutOfmemyException”的异常。

我在这台机器上有4 GB 的内存2.5 GB 是空闲的 当我开始这个运行时,电脑上显然有足够的空间来处理762兆的10000000个随机数字。我需要存储尽可能多的随机数给予可用的内存。当我去生产将有12 GB 的盒子,我想利用它。

CLR 是否将我限制在默认的最大内存开始? 我如何请求更多?

更新

我认为,如果这个问题是由于 内存碎片引起的,那么将它分解成更小的块并逐步增加内存需求会有所帮助,但它不是 I can't get past a total ArrayList size of 256mb regardless of what I do tweaking 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;
347584 次浏览

将 Windows 进程限制增加到3gb (通过 boot.ini 或 Vista 引导管理器)

你可能想看看这个: “ “Out Of Memory” Does Not Refer to Physical Memory”埃里克利伯特。

简而言之,非常简单地说,“内存不足”并不意味着可用内存的数量太少。最常见的原因是,在当前的地址空间中,没有足够大的连续内存部分来满足所需的分配。如果您有100个块,每个块大4 MB,那么当您需要一个5 MB 的块时,这样做是无济于事的。

要点:

  • 我们称之为“进程存储器”的数据存储器在我看来最好可视化为 磁盘上的大容量文件
  • RAM 可以看作仅仅是一种性能优化
  • 程序消耗的虚拟内存总量与其性能没有太大关系
  • "running out of RAM" seldom results in an “out of memory” error. Instead of an error, it results in bad performance because the full cost of the fact that storage is actually on disk suddenly becomes relevant.

You don't have a continuous block of memory in order to allocate 762MB, your memory is fragmented and the allocator cannot find a big enough hole to allocate the needed memory.

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

32bit windows has a 2GB process memory limit. The /3GB boot option others have mentioned will make this 3GB with just 1gb remaining for OS kernel use. Realistically if you want to use more than 2GB without hassle then a 64bit OS is required. This also overcomes the problem whereby although you may have 4GB of physical RAM, the address space requried for the video card can make a sizeable chuck of that memory unusable - usually around 500MB.

我也遇到过类似的大数据集问题,强迫应用程序使用如此多的数据并不是一个正确的选择。我能给你的最好的建议是,如果可能的话,以小块的形式处理你的数据。因为处理这么多的数据,问题迟早会回来。另外,您无法知道运行您的应用程序的每台计算机的配置,因此总是存在异常发生在另一台计算机上的风险。

我建议不要使用/3GB 的 windows 引导选项。除了其他一切(对于表现不好的 应用程序这样做是过分的,而且它可能无论如何都不会解决您的问题) ,它会导致很多不稳定性。

许多 Windows 驱动程序没有使用这个选项进行测试,因此很多驱动程序都假定用户模式指针总是指向地址空间的较低的2GB。这意味着它们可能会因为/3GB 而崩溃。

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

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

分配2400 MB 的块可能会更好。或者4200MB 的块。较小的分配更容易在分散的内存空间中找到空间。

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

If you need such large structures, perhaps you could utilize Memory Mapped Files. 这篇文章可能会有所帮助: Http://www.codeproject.com/kb/recipes/memorymappedgenericarray.aspx

德扬

与其分配一个大型数组,你能尝试使用迭代器吗?它们是延迟执行的,这意味着只有在 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 语句的要求生成随机数。这样就不会耗尽内存。

或者,如果您必须将它们放在一个地方,那么将它们存储在一个文件中,而不是存储在内存中。

从32位改为64位对我来说很有效-如果你是在一台64位的电脑上并且不需要移植,那么值得一试。

正如您可能已经发现的那样,问题在于您正试图分配一个大的连续内存块,由于内存碎片,这个内存块无法工作。如果我需要做你正在做的事情,我会做以下事情:

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)存储在一个变量中,然后当你开始浏览列表时,你会使用原始的种子创建一个新的 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);
}

值得注意的是,虽然 Fredrik Mörk 的回答中链接的博客表明这个问题通常是由于缺少 地址空间引起的,但它并没有列出其他一些问题,比如2GB CLR 对象大小限制(ShuggyCoUk 在同一个博客上的评论中提到过) ,掩盖了内存碎片,也没有提到页面文件大小的影响(以及如何使用 CreateFileMapping功能解决这个问题)。

2GB 的限制意味着 randomNumbers必须小于2GB。因为数组是类,它们本身有一些开销,这意味着 double的数组需要小于2 ^ 31。我不确定长度比2 ^ 31小多少,但是 一个.NET 数组的开销?表示12-16字节。

内存碎片与硬盘碎片非常相似。您可能有2GB 的地址空间,但是在创建和销毁对象时,这些值之间会有间隙。如果这些间隙对于您的大型对象来说太小,并且不能请求额外的空间,那么您将得到 System.OutOfMemoryException。例如,如果您创建了200万个1024字节的对象,那么您将使用1.9 GB。如果你删除每一个地址不是3的倍数的对象,那么你将使用.6 GB 的内存,但是它将分散在地址空间中,中间有2024字节的打开块。如果您需要创建一个.2 GB 的对象,那么您将无法执行此操作,因为没有足够大的块来容纳它,而且无法获得额外的空间(假设是32位环境)。这个问题的可能解决方案是使用更小的对象,减少存储在内存中的数据量,或者使用内存管理算法来限制/防止内存碎片。应该注意的是,除非您正在开发一个使用大量内存的大型程序,否则这将不是一个问题。此外,这个问题可能出现在64位系统上,因为窗口主要受到页面文件大小和系统上 RAM 数量的限制。

由于大多数程序请求操作系统的工作内存,而不请求文件映射,因此它们将受到系统的 RAM 和页面文件大小的限制。正如 Néstor Sánchez (N & # 233; stor S & # 225; nchez)在博客上的评论中指出的那样,使用 C # 这样的托管代码,你会受到 RAM/页面文件限制和操作系统地址空间的限制。


这比预期的时间长多了。希望能帮到别人。我发布这篇文章是因为我在一个拥有24GB 内存的系统上运行 x64程序时遇到了 System.OutOfMemoryException,尽管我的数组只有2GB 内存。

检查您是否正在构建64位进程,而不是32位进程,后者是 VisualStudio 的默认编译模式。为此,右键单击项目 Properties-> Build-> Platform target: x64。与任何32位进程一样,用32位编译的 VisualStudio 应用程序的虚拟内存限制为2GB。

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

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

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

我也遇到过类似的问题,这是由于 StringBuilder.ToString () ;

将解决方案转换为 x64。如果仍然面临问题,对抛出异常的所有内容赋予 max length,如下所示:

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

如果不需要 VisualStudio 宿主进程:

取消选中选项: Project-> Properties-> Debug-> Enable the VisualStudio HostingProcess

然后建造。

如果你仍然面临这个问题:

转到 Project-> Properties-> Build Events-> Post-Build Event 命令行并粘贴以下内容:

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86
"$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)"  /LARGEADDRESSAWARE

现在,构建项目。