.NET框架如何为OutOfMemoryException分配内存?

145
在C++中,实际上可以通过值抛出异常而不在堆上分配内存,因此这种情况是有意义的。但在.NET框架中,OutOfMemoryException是引用类型,因此它在堆上分配。当没有足够的内存创建新对象时,.NET框架如何为OutOfMemoryException分配内存?

6
很好的问题。也许有足够的内存专门为那种情况保留。 - GreatAndPowerfulOz
19
补充其他答案,OOM表示无法分配您请求的块。如果您要求100MB,但运行时能找到的最大可用块只有99MB,它将失败。但是,OOM异常只需要一些字节的内存。因此,即使您的分配失败,也不意味着没有剩余内存。但当然,运行时可能会保留一些内存来应对这种情况。 - Jason Williams
4
顺便提一下,你对C++的假设是错误的。根据编译器的不同,异常可能会被分配到堆上。微软编译器不会这样做,但在常见的C ++ ABI中,异常是在堆上分配的。如果堆上没有空间了,则会使用一个小的预分配紧急缓冲区。请注意,这不适用于MS编译器。 - Sebastian Redl
2个回答

164

这是运行时预先分配的。如果您探索任何托管进程的堆,您将找到该异常的实例。

这是一个Hello World应用程序的预分配异常:

0:003> !dumpheap -stat -type Exception
Statistics:
      MT    Count    TotalSize Class Name
735f2920        1           84 System.ExecutionEngineException
735f28dc        1           84 System.StackOverflowException
735f2898        1           84 System.OutOfMemoryException
735f2744        1           84 System.Exception
735f2964        2          168 System.Threading.ThreadAbortException

4
但是会调用“OutOfMemoryException”的构造函数。 - Tim Schmelter
36
运行时不必遵循与你的代码相同的规则。另一个例子是,如果你抛出 StackOverflowException 异常,你可以捕捉它,但如果运行时抛出该异常,你无法捕捉它(默认情况下)。 - Brian Rasmussen
8
CLR的许多基础机制实际上是用“C”和“C ++”编写的。因此,完全有可能对象是“new'd in place”,或者内存以其他方式进行操作。 - GreatAndPowerfulOz
2
@hvd:这会带来什么副作用?OOM是否会给出堆栈跟踪?我认为其余信息应该是相当静态的? - James Barrass
7
如果两个线程同时抛出相同类型的异常,会不会需要两个相同类型的异常来处理? - ASA
显示剩余3条评论

42
当运行时内部遇到内存不足的情况时,它会调用ThrowOutOfMemory。这将调用Exception::GetOOMException,该函数在堆栈上构建对象,然后将其复制到静态分配的全局实例中,然后再抛出异常。
但这不是托管的异常,而是在ex.h中声明的C++异常。 C++异常在clrex.cpp中转换为托管异常,其中包含特定代码以抛出预先分配的托管OutOfMemoryException,该异常最初是在appdomain.cpp中分配和构造的。
注意:其中一些源文件很大,在加载语法高亮显示时可能会使浏览器挂起几秒钟。

在另一个答案的评论中,Tim Schmelter链接的调用网站与运行时内存耗尽并且无法构造对象无关。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接