【问题标题】:Linux Memory mapped files reserve lots of physical memoryLinux内存映射文件保留大量物理内存
【发布时间】:2010-09-24 16:16:06
【问题描述】:

我在多个线程中描述了一个问题,该问题涉及 Linux 下的内存映射和不断增长的内存消耗。

当我在 Linux 或 MacOS X 下打开一个 1GB 的文件并将其映射到内存时使用

me.data_begin = mmap(NULL, capacity(me), prot, MAP_SHARED, me.file.handle, 0);

并顺序读取映射内存,我的程序使用了越来越多的物理内存,尽管我使用了 posix_madvise(甚至在读取过程中多次调用它):

posix_madvise(me.data_begin, capacity(me), MMAP_SEQUENTIAL);

没有成功。 :-(

我试过了:

  • 不同的标志 MMAP_RANDOM、MMAP_DONTNEED、MMAP_NORMAL 没有成功
  • posix_fadvise(me.file.handle, 0, capacity(me), POSIX_FADV_DONTNEED) 在调用 mmap 前后 -> 不成功

在 Mac OS X 下工作 !!!当我结合

posix_madvise(.. MMAP_SEQUENTIAL)

msync(me.data_begin, capacity(me), MS_INVALIDATE).

常驻内存低于16M(我在16mio步骤后定期调用msync)。

但在 Linux 下没有任何效果。有人对我在 Linux 下的问题有想法或成功案例吗?

干杯, 大卫

【问题讨论】:

  • 它可能相关也可能不相关,但了解以下信息应该很有用:您使用的是 32 位还是 64 位系统?你知道你不应该在 32 位系统中映射 1GB 吗? (即使您使用的是 64 位系统,您也可能会担心可移植性)。
  • 所有系统都是 64 位(带有 64 位文件指针和偏移量),我可以成功映射 40GB 文件。为了重现性,我只是将问题归结为 1GB。
  • @Sven。在某些情况下使用内存映射是不可避免的,例如当库调用需要内存区域而不是文件时。所以你的建议没有帮助,也没有回答问题。至于答案,显然在 Linux MMAP_SEQUENTIAL 上几乎是broken。预读部分有效,页面回收部分无效。向 Linux 建议事实上这些页面是很好的候选者的唯一方法是取消映射该区域(并再次映射它)。

标签: linux macos mmap


【解决方案1】:

Linux 内存管理不同于其他系统。关键原则是,未使用的内存是浪费的内存。在许多方面,Linux 都试图最大限度地利用内存,从而(大多数时候)获得更好的性能。

并不是说在 Linux 中“什么都行不通”,而是它的行为与你预期的有点不同。

当从 mmapped 文件中提取内存页面时,操作系统必须决定它将释放(或换出)哪些物理内存页面以便使用。它将寻找更容易换出(不需要立即写入磁盘)并且不太可能再次使用的页面。

madvice() POSIX 调用用于告诉系统您的应用程序将如何使用页面。但正如其名称所说,这是一个建议,以便操作系统更好地进行分页和交换决策。这既不是政策也不是命令。

为了演示 madvice() 在 Linux 上的效果,我修改了我给学生的一个练习。请参阅complete source code here。我的系统是 64 位的,有 2 GB 的 RAM,现在大约 50% 正在使用中。使用该程序来映射一个 2 GB 的文件,按顺序读取它并丢弃所有内容。它每读取 200 MB 报告 RSS 使用情况。结果没有madvice()

<juliano@home> ~% ./madvtest file.dat n
     0 :     3 MB
   200 :   202 MB
   400 :   402 MB
   600 :   602 MB
   800 :   802 MB
  1000 :  1002 MB
  1200 :  1066 MB
  1400 :  1068 MB
  1600 :  1078 MB
  1800 :  1113 MB
  2000 :  1113 MB

Linux 不断将内容从内存中推出,直到读取了大约 1 GB。之后,它开始对进程本身施压(因为其他 50% 的内存被其他进程激活)并稳定到文件结束。

现在,使用 madvice()

<juliano@home> ~% ./madvtest file.dat y
     0 :     3 MB
   200 :   202 MB
   400 :   402 MB
   600 :   494 MB
   800 :   501 MB
  1000 :   518 MB
  1200 :   530 MB
  1400 :   530 MB
  1600 :   530 MB
  1800 :   595 MB
  2000 :   788 MB

请注意,Linux 决定将页面分配给进程,直到它达到大约 500 MB,这比没有 madvice() 时要快得多。这是因为在那之后,当前在内存中的页面似乎比被此进程标记为顺序访问的页面更有价值。 VMM 中有一个阈值,用于定义何时开始从进程中删除旧页面。

您可能会问,为什么 Linux 一直在分配高达 500 MB 左右的页面并且没有很快停止,因为它们被标记为顺序访问。要么系统有足够的空闲内存页面,要么其他驻留页面太旧而无法保留。在将似乎不再有用的旧页面保留在内存中,以及为现在运行的程序提供更多页面之间,Linux 选择了第二个选项。

即使它们被标记为顺序访问,也只是一个建议。应用程序可能仍想返回这些页面并再次阅读它们。或系统中的另一个应用程序。 madvice() 调用仅说明应用程序本身在做什么,Linux 会考虑大局。

【讨论】:

  • 谢谢 Juliano,50% 的行为很有趣。我只是想知道为什么没有办法强制 Linux 释放我不再阅读的页面。相反,它牺牲了文件系统的缓冲区和缓存。在 MacOS X 上,牺牲这些缓冲区会使系统停顿,直到它完全无法使用。但幸运的是,我们可以通过 msync(... MS_INVALIDATE) 防止这种情况发生
  • @Dave:考虑过早释放这些页面是没有意义的。 Linux 并没有牺牲缓存和缓冲区,相反,它正在这样做。当您从磁盘读取更多数据时,Linux 无论如何都必须将这些数据带入内存。它有点缓存从磁盘读取的内容,但不是将其视为“缓存”,而是将其视为映射该文件的进程的 RSS 部分的一部分。当 Linux 再次需要缓存时,它将释放映射到该应用程序的那些页面。你不必担心这个!
  • @Juliano:考虑 MADV_SEQUENTIAL 明确告诉系统页面将通过顺序读取仅访问一次。这些页面是回收的完美候选者。相反,我在我的盒子上看到,直到达到 50% 的内存(在这种情况下为 32GB),文件缓存正在被回收。而且我看到其他进程的性能正在下降,现在我发现了一种强制 linux 不这样做的可笑方式。通过再次取消映射和映射文件,每 1Gb 左右。 DOES 解决了问题,之后我看不到其他进程的性能下降。
猜你喜欢
  • 2014-03-04
  • 2010-12-09
  • 2011-02-18
  • 1970-01-01
  • 2017-11-16
  • 2020-07-07
  • 2017-10-19
  • 2011-05-11
  • 1970-01-01
相关资源
最近更新 更多