为了账号安全,请及时绑定邮箱和手机立即绑定

当仍有大量可用内存时,抛出“ System.OutOfMemoryException”

当仍有大量可用内存时,抛出“ System.OutOfMemoryException”

C#
喵喵时光机 2019-11-12 10:59:55
这是我的代码:int size = 100000000;double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mbdouble[] randomNumbers = new double[size];异常:引发了类型为'System.OutOfMemoryException'的异常。我在这台机器上有4GB内存,当我开始运行时有2.5GB可用空间,显然PC上有足够的空间来处理762mb的100000000随机数。给定可用内存,我需要存储尽可能多的随机数。当我投入生产时,包装盒上将有12GB,我想利用它。CLR是否将我限制为默认的最大内存开始?以及我如何要求更多?更新资料我认为如果问题是由于内存碎片造成的话,将它分成较小的块并逐步增加我的内存需求会有所帮助,但是不管我做什么调整blockSize,我都无法超过256mb的ArrayList大小。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;
查看完整描述

3 回答

?
有只小跳蛙

TA贡献1824条经验 获得超8个赞

您可能需要阅读以下内容:Eric Lippert的“ “内存不足”不涉及物理内存 ”。

简而言之,并且非常简化,“内存不足”并不真正意味着可用内存量太小。最常见的原因是,在当前地址空间内,没有连续的内存部分足够大以服务于所需的分配。如果您有100个块,每个块4 MB,那么当您需要一个5 MB块时,这将无济于事。

关键点:

  • 在我看来,我们称之为“进程存储器”的数据存储最好可视化为磁盘上海量文件

  • RAM可以看作仅仅是性能优化

  • 程序消耗的虚拟内存总量实际上与它的性能没有太大关系

  • 很少“用完RAM”会导致“内存不足”错误。它导致错误的性能,而不是错误,因为存储实际上在磁盘上这一事实的全部成本突然变得很重要。


查看完整回答
反对 回复 2019-11-12
?
慕少森

TA贡献2019条经验 获得超9个赞

您可能已经发现,问题在于您正在尝试分配一个大的连续内存块,由于内存碎片,该内存块不起作用。如果我需要做您正在做的事情,请执行以下操作:


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);

}

重要的是要注意,尽管该问题通常是由于地址空间不足所致,但并未列出其他许多问题,例如2GB CLR对象大小限制(在ShuggyCoUk在同一博客上)掩盖了内存碎片,并且没有提及页面文件大小的影响(以及如何使用CreateFileMapping函数解决)。


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


内存碎片与HDD碎片非常相似。您可能有2GB的地址空间,但是在创建和销毁对象时,这些值之间会有间隙。如果这些间隙对于您的大物件而言太小,并且无法请求额外的空间,那么您将获得System.OutOfMemoryException。例如,如果您创建200万个1024字节的对象,则您使用的是1.9GB。如果删除地址不是3的倍数的每个对象,则将使用.6GB的内存,但是它将在整个地址空间中分布,中间有2024字节的开放块。如果您需要创建一个大小为.2GB的对象,则将无法执行该操作,因为没有足够大的块可容纳该块,并且无法获得额外的空间(假定为32位环境)。解决此问题的方法可能是使用较小的对象,减少存储在内存中的数据量或使用内存管理算法限制/防止内存碎片。应该注意的是,除非您正在开发使用大量内存的大型程序,否则这将不是问题。也,


由于大多数程序从操作系统请求工作内存,并且不请求文件映射,因此它们将受到系统RAM和页面文件大小的限制。


那比预期更长。希望它可以帮助某人。我发布它是因为我遇到了在System.OutOfMemoryException具有24GB RAM的系统上运行x64程序的问题,即使我的阵列仅容纳2GB的东西。


查看完整回答
反对 回复 2019-11-12
  • 3 回答
  • 0 关注
  • 722 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信