虚拟大小导致程序内存不足

Virtual Size causing program to run out of memory

本文关键字:程序 内存不足 虚拟      更新时间:2023-10-16

我正在使用Windows上的C++构建的服务器应用程序在虚拟大小达到2GB左右时内存不足(32位应用程序,启用了大地址感知)。但是,我注意到专用字节要小得多。目前的统计数据是:

虚拟大小:2.6GB专用字节:1.6GB

这两个数字的差值为 1GB。所以我的问题是:

  1. 这 1GB 的差异代表什么?
  2. 我的应用程序是否由于虚拟大小或专用字节而内存不足?

我还通过 VMMap 实用程序运行了我的应用程序,我注意到"私有数据"通常比承诺的大小高一个数量级。换句话说,私有数据的总大小可能是 200MB,但承诺的大小仅为 20MB。我不太确定什么是私人数据,但根据我迄今为止的研究,它似乎表明它只是堆的一部分。

编辑:

我已经使用Purify寻找内存泄漏,但我并没有真正找到任何有用的东西。没有指针的内存形式的内存泄漏似乎不是问题,但是内存保留时间过长的内存泄漏可能是一个问题,我还没有研究过。但是,关键是要了解为什么虚拟大小会导致内存不足问题。问题#1对我来说是理解这一点的最重要的。

这需要一点解释,所以请坚持在这里。

首先,这个话题是一个令人困惑的术语冲突的泥潭,所以请抛弃你对"虚拟内存"与磁盘有关的所有概念。

    物理内存
  • 是存储在物理设备上的内存。这通常是指系统RAM,但也可以是磁盘缓冲区,NIC缓冲区,显卡VRAM等。
  • 虚拟内存
  • 是映射到用户模式(虚拟)地址范围的一组物理地址范围,以便能够以安全和分隔的方式访问内存

关于我们为什么这样做快速说明:如果我们为进程提供直接内存地址,我们只能(可行地)拥有单个内存存储。这很不方便,并且不利于性能。当虚拟地址转换为系统内存 (RAM) 范围之外的物理地址时,处理器会发出页面错误。这会向操作系统中的中断处理程序发出信号,然后可以将内存访问操作委托给其他设备。有用!

在 32 位 Windows 系统上,进程在任何一个时间点可以寻址的最大虚拟内存量为 2GB。使用 AWE 可以增加到 3GB,或者使用 /4GT 和 AWE 增加到 4GB。这并不意味着一个进程只能分配 2GB(或 3GB/4GB,具体取决于前面讨论的设置)的内存。这只是意味着它不能并发访问更多。

例如,如果打开大小为 1GB 的内存映射文件,则虚拟内存使用量将增加 1GB。您没有接触 RAM 或磁盘,但您已为进程分配了虚拟地址空间块。如果要在提供此内存映射文件的同时分配 1.1GB 的 RAM,则不能。必须先从流程中取消映射文件。请记住,内存仍可以保持已分配状态并充满数据,但实际上并未映射到进程中。如果您的机器上有 8GB 的 RAM,您可以用数据填充 6GB 并将其中的 2GB 映射到您的进程中。当您需要使用该内存的其他部分时,必须取消映射现有块并映射其他部分。

因此,对于您所看到的差异:

  1. 专用字节告诉您进程已映射的虚拟设备内存的字节数,不包括与其他进程共享的虚拟内存(例如映射的文件、全局堆等)。

  2. 工作集告诉您正在使用多少字节的物理内存。这包括物理内存、设备缓冲区和映射文件。这是一个非常奇怪的数字,因为它等同于触摸的物理内存+映射的虚拟非系统内存。通常,您应该完全忽略此数字。它对于调试内存泄漏实际上毫无用处。

  3. 虚拟
  4. 字节是已映射的虚拟内存总量。

不同之处在于,您已将共享虚拟内存(例如一堆 DLL 文件或全局堆块)映射到进程中。差异表明这些共享映射的总大小约为 1GB。

请记住,这些与交换无关,交换是将系统内存页传输到磁盘(所谓的"页面文件")以提高快速系统资源 (RAM) 的可用性。该文件的命名在Windows的这一领域引起了无休止的混乱,当Microsoft最终决定将其称为"交换"而不是"虚拟内存"或"页面文件"时,我会很高兴。

    虚拟
  1. 大小与专用字节解释:什么是私有字节、虚拟字节、工作集?(见下面的引文)
  2. 您的应用程序可能会达到 2 GB 的虚拟大小限制,特别是因为您自己也看到了这种行为
  3. /LARGEADDRESSAWARE仅在系统本身在启用/3GB AKA 4GT 的情况下启动时,才在Win32操作系统中扩展应用程序的虚拟大小限制

虚拟字节是 整个过程。这就像工作集,从某种意义上说,它 包括内存映射文件(共享 DLL),但也包括数据 在备用列表中,并且已分页且 位于磁盘某处的页面文件中。使用的总虚拟字节数 通过系统上在重负载下的每个进程加起来将 比机器实际拥有的内存多得多。

所以关系是:

  • 专用字节是应用实际分配的内容,但包括页面文件使用情况;
  • 工作集是非分页专用字节加上内存映射文件;
  • 虚拟字节是工作集加上分页专用字节和备用列表。

我的机器上遇到了类似的问题,即C/C++/.NET win32应用程序内存不足。它消耗了所有2GB的虚拟地址。它看起来像虚拟地址耗尽,因为进程资源管理器仅显示应用程序占用的大约 900MB 内存。VMMap显示了1.6GB的私有数据和大约700MB的未提交内存。

原因很简单。应用程序验证程序 (C:\Windows\SysWOW64\appverif.exe),它被配置为测试应用程序(基本测试被标记为 - vfbasics.dll)。从应用程序验证程序中删除应用程序后,它工作正常。

只是一个评论:启用大地址感知仅向操作系统发出信号,表明它可以以 3:1 的方式安全地划分从该特定可执行文件创建的进程的虚拟地址空间,而不是通常的 2:2。应用程序应明确标识它支持 3:1 除法的原因有多种,但最明显的原因是,在 2:2 模式下,探测虚拟地址的 MSB 可用于测试该地址是属于虚拟地址空间的内核部分还是属于用户部分。在 3:1 中,此测试不再有效,因为 MSB 也1虚拟地址空间用户部分的 1/3。这要求此应用程序可能调用的驱动程序还应知道可能的 3:1 划分,并应使用另一种方法来测试给定的虚拟地址是否属于 not 的用户空间。

你仍然必须明确地告诉内核启用对 3:1 VA 空间划分的支持,正如 Roman R 所指出的那样。