【问题标题】:virtual v. physical memory in assessing C/C++ memory leak评估 C/C++ 内存泄漏时的虚拟内存和物理内存
【发布时间】:2012-06-05 16:22:24
【问题描述】:

我有一个 C++ 应用程序,我正试图解决内存泄漏问题,但我意识到我并不完全理解虚拟内存和物理内存之间的区别。

top 的结果(因此 16.8g = 虚拟,111m = 物理):

4406 um 20 0 16.8g 111m 4928 S 64.7 22.8 36:53.65 client

我的进程拥有 500 个连接,每个用户一个,在这些数字上,这意味着每个用户大约有 30 MB 的虚拟开销。在不深入了解我的应用程序的细节的情况下,如果我不知道虚拟内存的实际含义,那么将所有向量、结构、线程、堆栈上的函数等加在一起听起来很现实的唯一方法是。顺便说一句,没有 -O 优化标志。

所以我的问题是:

  • C++ 中的哪些操作会使虚拟内存膨胀这么多?
  • 如果我的任务正在使用 gigs 虚拟内存,会不会有问题?
  • 堆栈和堆函数变量、向量等 - 这些是否必然会增加物理内存的使用?
  • 消除内存泄漏(通过deletefree() 等)是否一定会减少物理和虚拟内存的使用?

【问题讨论】:

  • 您是否尝试过使用像 valgrind 这样的内存分析器?使用 top 检查是否有泄漏并不理想。
  • 我的同事告诉我,考虑到我们正在使用的库,valgrind 已经非常没用了(我还需要自己对此进行调查......这是待办事项)。现在,我正在尝试了解我正在尝试获取的实际数字并在源代码分析级别工作 - 当这变得毫无结果时,我将引入工具。
  • 无优化 = 您的数据无关紧要。
  • @DeadMG - 据我了解 -O1 和 -O2 优化速度并增加任务规模。因此,如果我在关闭它们的情况下遇到任务大小问题,那么开启它们只会变得更糟。

标签: c++ memory memory-leaks ram virtual-memory


【解决方案1】:

虚拟内存是你的程序处理的。它由mallocnew 等人返回的所有地址组成。每个进程都有自己的虚拟地址空间。理论上,虚拟地址的使用受限于程序的地址大小:32 位程序有 4GB 的地址空间; 64 位程序的功能要多得多。实际上,进程可以分配的虚拟内存量小于这些限制。

物理内存是焊接到主板上或安装在内存插槽中的芯片。在任何给定时间使用的物理内存量都受限于您计算机中的物理内存量。

虚拟内存子系统将程序使用的虚拟地址映射到 CPU 发送到 RAM 芯片的物理地址。在任何特定时刻,您分配的大部分虚拟地址都未映射;因此物理内存使用低于虚拟内存使用。如果访问的是已分配但未映射的虚拟地址,操作系统会无形地分配物理内存并将其映射进去。当您不访问虚拟地址时,操作系统可能会取消映射物理内存。

依次回答您的问题:

  • C++ 中的哪些操作会如此膨胀虚拟内存?

newmalloc,大数组的静态分配。通常任何需要在程序中使用内存的东西。

  • 如果我的任务正在使用 gigs 虚拟内存,会不会有问题?

这取决于您的程序的使用模式。如果您分配了您从未接触过的大量内存轨道,并且您的程序是 64 位程序,那么您可以使用大量虚拟内存。

另外,如果你的内存使用量无限制地增长,你最终会耗尽一些资源。

  • 堆栈和堆函数变量、向量等 - 这些是否必然会增加物理内存的使用?

不一定,但很有可能。触摸变量的行为确保,至少暂时,它(以及“靠近”它的所有内存)都在物理内存中。 (另外:像std::vector 这样的容器可以分配在堆栈或堆上,但包含 对象分配在堆上。)

  • 消除内存泄漏(通过 delete 或 free() 等)是否必然会减少物理和虚拟内存的使用?

物理:可能。虚拟:是的。

【讨论】:

  • 因此,“硬”内存泄漏(对内存的引用从程序中消失)很可能会保留在虚拟内存中,但会从物理内存中消失并远离。我是否有可能用完物理内存,或者处理器只需要从虚拟 -> 物理进行更多交换?换句话说,内存泄漏(根据定义意味着虚拟)能否在不耗尽 64 位地址空间的情况下降低我的任务?
  • 是的,内存泄漏可以在不耗尽地址空间的情况下降低您的任务。您可能会用完交换空间,您可能会使物理内存如此稀缺,以至于系统不断交换(也称为“页面抖动”)。您可能会用完维护虚拟物理交换映射的操作系统资源。
  • "从物理内存中清除" - 具体来说,数据被写入交换设备,然后物理内存被重新用于其他目的。在程序终止之前,虚拟内存地址和交换设备上的磁盘扇区都不能重用。
【解决方案2】:

虚拟内存是进程使用的地址空间。每个进程都有一个指针的 64 位(或 32 个,取决于架构)可寻址字节的完整视图,但并非每个字节都映射到真实的东西。操作系统管理将虚拟地址映射到实际物理内存页面的表——或者任何该地址的真正含义(无论它看起来是您的应用程序的内存)。例如,对于您的应用程序,一个地址可能指向某个函数,但实际上它还没有从磁盘加载,当您调用它时,它会产生一个页面错误中断,内核通过从可执行文件并将其映射到应用程序的地址空间,以便执行。

从 Linux 的角度来看(我相信大多数现代操作系统):

  • 分配内存会使虚拟内存膨胀。实际上使用分配的内存会增加物理内存使用量。这样做太多,它会被交换到磁盘,最终你的进程将被杀死。
  • mmaping 文件只会增加虚拟内存的使用,这包括可执行文件的大小:它们越大,使用的虚拟内存就越多。
  • 用尽虚拟内存的唯一问题是您可能已将其耗尽。这主要是 32 位系统的问题,您只有 4gb(其中 1gb 是为内核保留的,因此应用程序数据只有 3gb)。
  • 在堆栈上分配变量的函数调用可能会增加物理内存使用量,但您(通常)不会泄漏此内存。
  • 分配的堆变量占用虚拟内存,但只有在您对它们进行读/写时才会真正获得物理内存。
  • 释放或删除变量不一定会减少虚拟/物理内存消耗,它取决于分配器内部,但通常会。

【讨论】:

    【解决方案3】:

    您可以设置以下环境变量来通过 malloc 控制内部内存分配。设置后,它将回答所有四个问题。如果您想了解其他选项,请参考:

    http://man7.org/linux/man-pages/man3/mallopt.3.html

    导出 MALLOC_MMAP_THRESHOLD_=8192

    导出 MALLOC_ARENA_MAX=4

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多