增加虚拟内存而不增加VmSize

时间:2012-11-09 13:08:09

标签: linux fortran mpi

我在Google和此网站上搜索了我的问题,但我仍然不理解解决方案。

我有一些MPI程序RECV一些数据。程序在大型阵列上崩溃且虚拟内存不足,因此我开始考虑/proc/self/status文件。

MPI_RECV之前:

Name:   model.exe                                                               
VmPeak:   841640 kB
VmSize:   841640 kB
VmHWM:     15100 kB
VmRSS:     15100 kB
VmData:   760692 kB

之后:

Name:   model.exe                                                            
VmPeak:   841640 kB
VmSize:   841640 kB
VmHWM:    719980 kB
VmRSS:    719980 kB
VmData:   760692 kB

我在Ubuntu上测试它并通过系统监视器看到这个内存增加了。但我很困惑,VmSize(和VmPeak)参数没有变化。

问题是 - 实际内存使用情况的指标是什么?

这是否意味着,真正的指标是VmRSS? (仅VmSize已分配,但仍未使用内存)

1 个答案:

答案 0 :(得分:23)

(问题的可能解决方案是最后一段)

具有虚拟内存的大多数现代操作系统上的内存分配是一个两阶段过程。首先,保留进程的虚拟地址空间的一部分,并且进程的虚拟存储器大小(VmSize)相应地增加。这将在所谓的流程页面表中创建条目。页面最初与物理内存帧无关,即实际上没有使用物理内存。每当实际读取或写入此分配部分的某些部分时,就会发生页面错误,操作系统会从物理内存安装(映射)一个空闲页面。这会增加进程的驻留集大小(VmRSS)。当某些其他进程需要内存时,操作系统可能会将某些不经常使用的页面的内容("不经常使用的页面的定义"高度依赖于实现)存储到某些持久存储(大多数情况下是硬盘驱动器,或者一般到交换设备)然后取消映射。此过程会降低RSS但保持VmSize完整。如果稍后访问此页面,将再次发生页面错误并将其恢复。虚拟内存分配仅在释放虚拟内存分配时减少。请注意,VmSize也会计入内存映射文件(即可执行文件及其链接到的所有共享库或其他显式映射文件)和共享内存块。

进程中有两种通用类型的内存 - 静态分配的内存和堆内存。静态分配的内存保留所有常量和全局/静态变量。它是数据段的一部分,其大小由VmData指标显示。数据段还承载程序堆的一部分,其中正在分配动态内存。数据段是连续的,即它从某个位置开始并朝向堆栈向上增长(从非常高的地址开始然后向下增长)。数据段中堆的问题在于它由一个特殊的堆分配器管理,该分配器负责将连续的数据段细分为更小的内存块。另一方面,在Linux中,动态内存也可以通过直接映射虚拟内存来分配。这通常仅用于大型分配以节省内存,因为它只允许分配页面大小(通常为4 KiB)的倍数的内存。

堆栈也是大量内存使用的重要来源,尤其是在自动(堆栈)存储中分配大数组的情况下。堆栈从可用虚拟地址空间的最顶端开始并向下增长。在某些情况下,它可能会到达数据段的顶部,或者可能达到其他一些虚拟分配的末尾。那么糟糕的事情发生了堆栈大小在VmStack指标和VmSize中进行计算。 人们可以这样总结:

  • VmSize考虑了所有虚拟内存分配(文件映射,共享内存,堆内存,无论内存),并且几乎每次分配新内存时都会增长。几乎是因为如果新的堆内存分配是在数据段中释放的旧分配的位置进行的,则不会分配新的虚拟内存。无论何时释放虚拟分配,它都会递减。 VmPeak跟踪VmSize的最大值 - 它只能及时增加。
  • VmRSS随着内存的访问而增长,随着内存被分页到交换设备而减少。
  • VmData随着堆的数据段部分的使用而增长。它几乎从不收缩,因为当前的堆分配器保留释放的内存以防将来分配需要它。

如果您在具有InfiniBand或其他基于RDMA的结构的群集上运行,则会发挥另一种内存 - 锁定(已注册)内存(VmLck)。这是不允许被分页的内存。它的增长和缩小取决于MPI的实现。有些人从未取消注册已经注册的块(有关为什么太复杂而无法在此处描述的技术细节),其他人这样做是为了更好地利用虚拟内存管理器。

在您的情况下,您说您遇到了虚拟内存大小限制。这可能意味着此限制设置得太低,或者您遇到操作系统强加的限制。首先,Linux(以及大多数Unix)都有通过ulimit机制强制实施人为限制的方法。在shell中运行ulimit -v将告诉您虚拟内存大小的限制在KiB中。您可以使用ulimit -v <value in KiB>设置限制。这仅适用于当前shell及其子级,grandchilren等生成的进程。如果要在远程节点上启动,则需要指示mpiexec(或mpirun)将此值传播到所有其他进程。如果您在某些工作负载管理器(如LSF,Sun / Oracle Grid Engine,Torque / PBS等)的控制下运行您的程序,则会有控制虚拟内存大小限制的作业参数。最后但并非最不重要的是,32位进程通常限制为2 GiB的可用虚拟内存。