在 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 将传播此内存不足异常。 这些跳转存根对应于一种调用方法,该方法用于地址空间中相距 2GB 或以上的动态链接库(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后,恢复的唯一建议方法是重启应用程序。