这是我的代码:
int size = 100000000;
double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb
double[] randomNumbers = new double[size];
例外: 抛出了'System.OutOfMemoryException'类型的异常。
我在这台机器上有4GB内存2.5GB是免费的当我开始运行时,PC上有足够的空间来处理760mb的100000000随机数。我需要在给定可用内存的情况下尽可能多地存储随机数。当我投入生产时,盒子上将有12GB,我想要使用它。
CLR是否将我限制为默认的最大内存?以及如何申请更多?
更新
我认为将此分解为更小的块并逐渐增加我的内存需求会有所帮助,如果问题是由于内存碎片,但它不会我无法通过无论我做什么调整blockSize ,总ArrayList大小为256mb。
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;
答案 0 :(得分:126)
您可能需要阅读:““Out Of Memory” Does Not Refer to Physical Memory”作者Eric Lippert。
简而言之,非常简化,“Out of memory”并不意味着可用内存量太小。最常见的原因是在当前地址空间内,没有足够大的内存连续部分来满足所需的分配。如果你有100个块,每个4 MB大,当你需要一个5 MB的块时,这对你没有帮助。
要点:
答案 1 :(得分:25)
你没有连续的内存块来分配762MB,你的内存是碎片,分配器找不到足够大的空间来分配所需的内存。
答案 2 :(得分:23)
检查您是否正在构建64位进程,而不是32位进程,这是Visual Studio的默认编译模式。为此,右键单击您的项目,属性 - &gt;构建 - &gt;平台目标:x64。与任何32位进程一样,以32位编译的Visual Studio应用程序的虚拟内存限制为2GB。
64位进程没有此限制,因为它们使用64位指针,因此它们的理论最大地址空间(虚拟内存大小)为16艾字节(2 ^ 64)。实际上,Windows x64将进程的虚拟内存限制为8TB。然后,内存限制问题的解决方案是以64位编译。
但是,默认情况下,Visual Studio中对象的大小仍限制为2GB。您将能够创建多个组合大小将大于2GB的阵列,但默认情况下不能创建大于2GB的阵列。希望如果您仍然想要创建大于2GB的数组,可以通过向app.config文件添加以下代码来实现:
<configuration>
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
</configuration>
答案 3 :(得分:7)
正如您可能想到的那样,问题是您正在尝试分配一个大的连续内存块,由于内存碎片而无法正常工作。如果我需要做你正在做的事情,我会做以下事情:
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]
。
如果始终按顺序访问值,则另一个选项可能是使用the overloaded constructor来指定种子。通过这种方式,您可以获得一个半随机数(如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);
}
值得注意的是,虽然在FredrikMörk的回答中链接的博客表明问题通常是由于缺少地址空间,但它没有列出其他一些问题,例如2GB CLR对象大小限制(在同一博客上的ShuggyCoUk评论中提到),掩盖了内存碎片,并没有提到页面文件大小的影响(以及如何使用CreateFileMapping
function解决它)
2GB限制意味着randomNumbers
必须小于2GB。由于数组是类并且有一些开销,这意味着double
的数组需要小于2 ^ 31。我不确定长度必须小于2 ^ 31,但Overhead of a .NET array?表示12 - 16字节。
内存碎片与HDD碎片非常相似。您可能有2GB的地址空间,但在创建和销毁对象时,值之间会有间隙。如果这些间隙对于大型对象来说太小,并且无法请求额外的空间,那么您将获得System.OutOfMemoryException
。例如,如果您创建了200万个1024字节对象,那么您使用的是1.9GB。如果删除地址不是3的倍数的每个对象,那么你将使用.6GB的内存,但它将分散在地址空间中,其间有2024个字节的打开块。如果你需要创建一个0.2GB的对象,你将无法做到这一点,因为没有足够大的块来容纳它,并且无法获得额外的空间(假设是32位环境)。此问题的可能解决方案包括使用较小的对象,减少存储在内存中的数据量,或使用内存管理算法来限制/防止内存碎片。应该注意的是,除非您正在开发使用大量内存的大型程序,否则这不会成为问题。此外,这个问题可能出现在64位系统上,因为窗口主要受页面文件大小和系统上RAM量的限制。
由于大多数程序从操作系统请求工作内存并且不请求文件映射,因此它们将受系统的RAM和页面文件大小的限制。正如NéstorSánchez(NéstorSánchez)在博客上发表的评论所述,使用C#等托管代码,你会遇到RAM /页面文件限制和操作系统的地址空间。
这比预期的要长。希望它可以帮助某人。我发布它是因为我遇到System.OutOfMemoryException
运行x64程序的系统有24GB的RAM,即使我的阵列只有2GB的东西。
答案 4 :(得分:5)
我建议不要使用/ 3GB windows启动选项。除了其他一切(对于一个表现不佳的应用程序来说这样做太过分了,而且它可能无法解决你的问题),它会导致很多不稳定。
许多Windows驱动程序未使用此选项进行测试,因此其中相当一部分假设用户模式指针始终指向地址空间的较低2GB。这意味着它们可能会以/ 3GB的速度破坏。
但是,Windows通常会将32位进程限制为2GB的地址空间。 但这并不意味着你应该期望能够分配2GB!
地址空间已经遍布各种分配的数据。有堆栈,所有加载的程序集,静态变量等等。无法保证在任何地方都会有800MB的连续未分配内存。
分配2个400MB的块可能会更好。或者4个200MB的块。较小的分配更容易在碎片化的内存空间中找到空间。
无论如何,如果您打算将其部署到12GB的计算机上,您将希望将其作为64位应用程序运行,这应该可以解决所有问题。
答案 5 :(得分:3)
从32位改为64位对我来说非常有用 - 如果你使用的是64位个人计算机而且不需要移植就值得一试。
答案 6 :(得分:2)
如果你需要这么大的结构,也许你可以利用内存映射文件。 这篇文章可能有用: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx
LP, 德扬
答案 7 :(得分:1)
32位窗口有2GB进程内存限制。其他人提到的/ 3GB启动选项将使这个3GB仅剩1GB用于操作系统内核。实际上,如果你想毫不费力地使用2GB以上,那么就需要64位操作系统。这也解决了这个问题,即虽然你可能有4GB的物理内存,但视频卡所需的地址空间可能会使该内存的大量内存无法使用 - 通常大约为500MB。
答案 8 :(得分:1)
您可以尝试使用迭代器,而不是分配大型数组吗?这些是延迟执行的,意味着只有在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语句生成它们。你不会用这种方式耗尽内存。
或者,如果您必须将它们全部放在一个位置,请将它们存储在文件中而不是存储在内存中。
答案 9 :(得分:0)
好吧,我遇到了类似的大数据集问题,试图强制应用程序使用这么多数据并不是真正的选择。我能给你的最好的建议是尽可能以小块的形式处理你的数据。因为处理这么多数据,问题迟早会回来。另外,您无法知道将运行您的应用程序的每台计算机的配置,因此始终存在异常将在另一台计算机上发生的风险。
答案 10 :(得分:0)
我有类似的问题,这是由于StringBuilder.ToString();
答案 11 :(得分:0)
将您的解决方案转换为x64。如果您仍然遇到问题,请将抛出异常的所有内容授予最大长度,如下所示:
var jsSerializer = new JavaScriptSerializer();
jsSerializer.MaxJsonLength = Int32.MaxValue;
答案 12 :(得分:0)
如果不需要Visual Studio托管过程:
取消选中以下选项:Project-> Properties-> Debug->启用Visual Studio托管过程
然后构建。
如果您仍然遇到问题:
转到“项目”->“属性”->“构建事件”->“构建后事件”命令行,然后粘贴以下内容:
call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86
"$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)" /LARGEADDRESSAWARE
现在,构建项目。
答案 13 :(得分:-2)
将Windows进程限制增加到3gb。 (通过boot.ini或Vista启动管理器)