代码之家  ›  专栏  ›  技术社区  ›  ksm

.NET OutOfMemoryException

  •  12
  • ksm  · 技术社区  · 14 年前

    class OutOfMemoryTest02
    {
        static void Main()
        {
            string value = new string('a', int.MaxValue);
        }
    }
    

    抛出异常;但这不会:

    class OutOfMemoryTest
    {
        private static void Main()
        {
            Int64 i = 0;
            ArrayList l = new ArrayList();
            while (true)
            {
                l.Add(new String('c', 1024));
    
                i++;
            }
        }
    }
    

    9 回复  |  直到 13 年前
        1
  •  10
  •   Steve Townsend    14 年前

    int.MaxValue 在文件里?它相当于2GB的内存,这可能比一个连续的“a”字符块可用的内存要多——这就是您在这里要求的。

    http://msdn.microsoft.com/en-us/library/system.int32.maxvalue.aspx

    无限循环最终将导致相同的异常(或与过度使用RAM间接相关的另一个异常),但这需要一段时间。试着增加 1024 10 * 1024 * 1024 在循环案例中更快地再现症状。

    i ).

        2
  •  7
  •   ulrichb    10 年前

    你的

    new string('a', int.MaxValue);
    

    OutOfMemoryException string 有长度限制。中的“备注”部分 MSDN docs 说:

    一个字符串对象在内存中的最大大小是2GB,大约10亿个字符。

    在我的系统上(.NET 4.5 x64) new string('a', int.MaxValue/2 - 31) 抛出,然而 new string('a', int.MaxValue/2 - 32) 作品。

    虚拟地址空间 . 当这个到达时,你会得到一个 OutOfMemoryException异常 我也是。

    great article

        3
  •  5
  •   Moo-Juice    14 年前

    因为 int.MaxValue 是2147483647,或者,需要连续分配的2GB。

    在第二个例子中,操作系统每次只需要找到1024个字节来分配,并且可以交换到硬盘驱动器。我敢肯定,如果你让它运行足够长的时间,你会在一个黑暗的地方结束:)

        4
  •  4
  •   Kent Murra    14 年前

    这就是说,这么大的ArrayList应该会耗尽内存,但很可能您没有让代码运行足够长的时间来耗尽内存。

        5
  •  3
  •   Hans Passant    14 年前

    第二个片段也会崩溃。它只需要花费很长时间,因为它消耗的内存要慢得多。注意你的硬盘访问灯,当Windows从RAM中抽出页面腾出空间时,它会剧烈地闪烁。第一个字符串构造函数立即失败,因为堆管理器不允许分配4G字节。

        6
  •  2
  •   Tim Lloyd    14 年前

    这两个版本都会导致OOM异常,只是(在32位机器上)当你试图分配一个“单一”非常大的对象时,你会在第一个版本中立即得到它。

    第二个版本将花费更长的时间,但是由于以下几个因素,要达到OOM状态将需要大量的震荡:

    • 您将分配数百万个小对象,这些对象都可以由GC访问。一旦您开始给系统施加压力,GC将花费大量的时间扫描包含数百万个对象的代。这将需要相当长的时间,并开始对分页造成严重破坏,因为冷内存和热内存将随着对代的扫描而不断地调出。

    • 当GC扫描数百万个对象以尝试释放内存时,会出现页面抖动。扫描会导致大量的内存不断地被调出。

    抖动将导致系统停止处理开销,因此需要很长时间才能达到OOM条件。大部分时间将花在GC上,并为第二个版本进行分页。

        7
  •  1
  •   CaffGeek    14 年前

    在第一个示例中,您尝试一次性创建2g字符串

    它也不是一次全部存储在一个变量中。因此,我认为,您的一些内存使用可以持久化到磁盘上,为新数据腾出空间。

        8
  •  1
  •   Michael Stum    14 年前

    cannot have more than 2 GB

    首先是一些背景知识;在.Net运行时(CLR)的2.0版本中,我们做了一个有意识的设计决定,将GC堆中允许的最大对象大小保持在2GB,即使在64位版本的运行时上也是如此

    我不知道ArrayList在内部是如何工作的,但是您分配了多个每个2GB的对象,而据我所知,ArrayList只包含4个指针(x64上是8个?)字节,不管它们指向的对象有多大。

    引用 another article :

        9
  •  0
  •   Community CDub    8 年前

    您的系统可能会停止运行的一个原因是.NET的代码运行得更接近金属,而且您处于一个严格的循环中,如果进程优先级允许,它应该消耗100%的CPU。如果您想防止应用程序在执行紧循环时消耗过多的CPU,您应该添加如下内容 System.Threading.Thread.Sleep(10)

    JVM和.NET的CLR(公共语言运行时)之间的一个主要区别是CLR不限制x64系统/应用程序上的内存大小(在32位应用程序中,由于寻址限制,操作系统将所有应用程序限制为2GB)。JIT编译器为您的处理体系结构创建本机windows代码,然后在与任何其他windows应用程序相同的范围内运行它。JVM是一个更加孤立的沙盒,它根据配置/命令行开关将应用程序限制为指定的大小。

    当在x64环境中运行时,如果有足够的连续内存来分配包含所需的4GB,则不能保证单字符串创建失败内部最大值字符(.NET字符串默认为Unicode,每个字符需要2个字节)。32位应用程序总是会失败,即使设置了大地址感知标志,因为最大内存仍然是3.5GB)。

    如果您有足够的可用空间,那么代码的while循环版本可能会在抛出异常之前消耗更多的内存,因为字符串可以分配到较小的片段中,但最终肯定会遇到错误(尽管如果您有足够的资源,则可能会由于ArrayList超出数组中元素的最大数目,而不是不能为一个小字符串分配新的空间)。Kent Murra关于字符串内接也是正确的;您要么需要随机化字符串的长度或字符内容以避免内联,否则您只需创建指向同一字符串的指针。stevetownsend关于增加字符串长度的建议也会使找到足够大的连续内存块变得更加困难,这将使异常更快地发生。

    编辑:

    Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework

    Garbage Collection Part 2: Automatic Memory Management in the Microsoft .NET Framework

    以下是来自.NET垃圾收集开发人员的博客,以获取有关.NET内存管理更新版本的信息:

    So, what’s new in the CLR 4.0 GC?

    CLR 4.5: Maoni Stephens - Server Background GC

    这个问题可能有助于您观察.NET内存的内部工作原理:

    .NET Memory Profiling Tools

    推荐文章