在 64 位.NET Framework上运行的托管应用程序中的内存不足异常

如果托管应用程序面向 64 位 Microsoft .NET Framework 4.6.1,本文可帮助你解决内存不足异常。

原始产品版本:.NET Framework 4.6.1
原始 KB 编号: 3152158

症状

你有一个面向 64 位.NET Framework 4.6.1 的托管应用程序。 此应用程序从公共语言运行时 (CLR) 引发内存不足异常,并显示以下特定消息:

OutOfMemoryException:“指定的地址空间范围内内存不足,无法继续执行程序。”

原因

当代码管理器子系统无法在特定地址空间范围内为跳转存根分配内存时,CLR 会传播此内存不足异常。 (这些跳转存根对应于在动态链接库 (DLL) 地址空间中相距 2 GB 或更大的 DLL 之间调用的方法。) 调用方法的 2 GB 半径内必须有空间来存储 64 位方法调用的跳转存根。 应用程序无法从此特定错误中恢复。 因此,应用程序在遇到此错误后的状态是未知的,应将其视为已损坏。 恢复的唯一方法是重启应用程序。

解决方法

若要解决此问题,请使用以下设置方法之一:

  • 通过添加以下注册表项实现计算机范围的设置:

    • 位置: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
    • 类型:DWORD
    • 名称:NGenReserveForjumpStubs
    • 值:00000005
  • 通过将以下部分添加到应用程序配置文件来实现应用程序级设置:

    <configuration>
        <runtime>
            <NGenReserveForJumpStubs value="5" />
        </runtime>
    </configuration>
    

    注意

    NGenReserveForJumpStubs 导致 CLR 为每个已加载的 NGen 映像附近的跳转存根保留一定百分比的地址空间。 如果遇到此 OutOfMemoryException,建议使用值 5 或更大。

面向开发人员的信息

  • 出于性能原因,.NET Framework将方法调用编码为相对 32 位跳转。 在 64 位系统上,在地址空间) 中,调用方和被调用方之间的距离可能超过 2 GB (。 因为它超出了有符号 32 位偏移量的地址范围,因此 .NET 将在调用方 2 GB 内创建一个跳转存根。 然后,此跳转存根可以长跳转到 64 位地址空间中的任何位置。

  • JIT 和 NGen 缓解措施的工作方式略有不同。 两者都预先预留额外的地址空间,但进行此预留的点在两者之间有所不同。

  • NGenReserveForJumpStubs 是虚拟 NGen 映像大小的百分比 (percentReserveForJumpStubs) 。

  • 典型的跳转存根为 12 个字节。 有关详细信息,请参阅 JUMP_ALLOCATE_SIZE

  • 内存是在靠近加载 NGen 映像的地址附近分配和保留的, (确切的算法是 EEJitManager::EnsureJumpStubReserve。 当需要分配跳转存根,并且没有其他合适的可用地址空间时,将提交内存。

  • 前面提到的缓解措施不会修改 NGen 映像的内容。 NGen 映像具有相同的磁盘占用量,且没有缓解措施。

  • 目前没有很好的方法来检测应用程序何时接近限制。 可以监视 以确定 OutOfMemoryException 预留空间是否足够。

  • 即使存在大量未使用的内存,也可能会收到 OutOfMemoryException ,因为此特定错误与调用方 2-GB 地址范围半径内的内存可用性相关。

  • 请勿更改 的 CodeHeapReserveForJumpStubs默认值,因为它可能与上述问题无关。 我们尚未发现实际应用程序必须调整此设置作为解决方法的情况。

  • 将 设置为 NGenReserveForJumpStubs 更高的值可能会导致性能降低,并暴露其他细微问题的风险。

面向 IT 用户的信息

  • 其他版本的.NET Framework也可能出现此问题。 但是,解决方法目前仅适用于 .NET Framework 4.6.1。
  • 这是一个罕见的问题,只影响具有特定执行模式的大型工作负载。 超过 99% 的所有工作负载将遇到此问题。
  • 应用程序引发 后,建议恢复的唯一 OutOfMemoryException方法是重启应用程序。