正确确定 Linux 中的内存使用情况

Gol*_*wby 75 linux memory

我对从psfree看到的一些结果感到有些困惑。

在我的服务器上,这是结果 free -m

[root@server ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          2048       2033         14          0         73       1398
-/+ buffers/cache:        561       1486
Swap:         2047         11       2036
Run Code Online (Sandbox Code Playgroud)

我对 Linux 如何管理内存的理解是,它将磁盘使用情况存储在 RAM 中,以便每次后续访问都更快。我相信这是由“缓存”列指示的。此外,各种缓冲区存储在 RAM 中,如“缓冲区”列所示。

因此,如果我理解正确,“实际”用法应该是“-/+ 缓冲区/缓存”的“已使用”值,在这种情况下为 561。

所以假设所有这些都是正确的,抛出我的部分是ps aux.

我对ps结果的理解是,第 6 列 (RSS) 表示进程用于内存的大小(以千字节为单位)。

所以当我运行这个命令时:

[root@server ~]# ps aux | awk '{sum+=$6} END {print sum / 1024}'
1475.52
Run Code Online (Sandbox Code Playgroud)

结果不应该是“-/+ 缓冲区/缓存”的“已使用”列free -m吗?

那么,如何在 Linux 中正确确定进程的内存使用情况?显然我的逻辑有问题。

phe*_*mer 65

前几天无耻地从serverfault复制/粘贴我的答案:-)

linux 虚拟内存系统并不是那么简单。你不能只是添加了所有的RSS领域,并获得价值报告used通过free。造成这种情况的原因有很多,但我将介绍几个最大的原因。

  • 当进程分叉时,父进程和子进程都将显示相同的 RSS。但是 linux 使用写时复制,因此两个进程实际上都在使用相同的内存。只有当其中一个进程修改内存时,它才会真正被复制。
    这将导致free数字小于topRSS 总和。

  • RSS 值不包括共享内存。因为共享内存不属于任何一个进程,top所以不包括在 RSS 中。
    这将导致free数字大于topRSS 总和。

  • @ user239558 正如我在回答中提到的,数字不相加的原因有很多,我只列出了其中的 2 个。还有很多其他数字;缓存、平板、大页面等。 (2认同)
  • 可能在你回答这个问题多年后,我(至少)仍然有一个困惑。您说 RSS 值不包括共享内存,但是 [this answer](http://stackoverflow.com/a/21049737/1602266) 说“它确实包括共享库中的内存,只要来自共享库的页面图书馆实际上在内存中”。现在我不知道该相信哪一个……也许我在这里遗漏了一些细微的差异…… (2认同)
  • @Naitree“共享库”!=“共享内存”。共享内存是诸如“shmget”或“mmap”之类的东西。围绕内存的措辞非常棘手。在错误的地方使用错误的单词可能会完全搞乱句子的含义。 (2认同)

lem*_*eze 36

如果您正在寻找相加的内存数字,请查看smem

smem 是一个工具,可以提供大量关于 Linux 系统内存使用情况的报告。与现有工具不同,smem 可以报告比例集大小 (PSS),这是对虚拟内存系统中库和应用程序使用的内存量的更有意义的表示。

由于大部分物理内存通常在多个应用程序之间共享,因此称为常驻集大小 (RSS) 的内存使用的标准度量将大大高估内存使用。PSS 而是测量每个应用程序在每个共享区域中的“公平份额”,以提供现实的衡量标准。

例如这里:

# smem -t
  PID User     Command                         Swap      USS      PSS      RSS
...
10593 root     /usr/lib/chromium-browser/c        0    22868    26439    49364 
11500 root     /usr/lib/chromium-browser/c        0    22612    26486    49732 
10474 browser  /usr/lib/chromium-browser/c        0    39232    43806    61560 
 7777 user     /usr/lib/thunderbird/thunde        0    89652    91118   102756 
-------------------------------------------------------------------------------
  118 4                                       40364   594228   653873  1153092 
Run Code Online (Sandbox Code Playgroud)

PSS这里有趣的专栏也是如此,因为它考虑了共享内存。
不像RSS把它加起来是有意义的。我们在这里为用户级进程总共获得了 654Mb。

系统范围的输出告诉其余的:

# smem -tw
Area                           Used      Cache   Noncache 
firmware/hardware                 0          0          0 
kernel image                      0          0          0 
kernel dynamic memory        345784     297092      48692 
userspace memory             654056     181076     472980 
free memory                   15828      15828          0 
----------------------------------------------------------
                            1015668     493996     521672 
Run Code Online (Sandbox Code Playgroud)

所以总共1Gb RAM = 654Mb用户空间进程+ 346Mb内核内存+ 16Mb免费
(提供或占用几 Mb)

总体而言,大约一半的内存用于缓存 (494Mb)。

额外问题:这里的用户空间缓存与内核缓存是什么?


顺便说一句,视觉上的尝试:

# smem  --pie=name
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • 从你自己的链接来看,`smem` 显然自 2015 年以来就没有维护过,最后一次发布是在 2013 年。这仍然是推荐的工具吗?它在现代硬件上还有意义吗?您今天会推荐一个更好的最新工具吗? (2认同)

Byt*_*ger 17

一个非常好的工具是pmap列出某个进程的当前内存使用情况:

pmap -d PID
Run Code Online (Sandbox Code Playgroud)

有关它的更多信息,请参阅手册页,man pmap并查看每个系统管理员都应该知道的 20 个 Linux 系统监控工具,其中列出了我经常用来获取有关我的 Linux 机器的信息的出色工具。

  • @GoldenNewby 没有进程的“实际”内存使用情况。系统的实际内存使用情况是 `free` 告诉你的。 (3认同)

2bc*_*2bc 10

运行顶部,点击h寻求帮助,然后f添加字段。您可以添加以下字段:

  • RSS 应用程序使用的物理内存量
  • CODE 进程的可执行代码正在使用的内存总量
  • DATA - 专用于进程数据和堆栈的内存总量 (kb)

在这 3 个之间,您应该有非常准确的结果。您还可以使用我推荐的 tophtopatop.

编辑:如果你想要真正详细的信息,几乎忘记了。找到 PID 并找到以下文件。

PID=123

cat /proc/123/status

编辑 2:如果您能找到或拥有这本书:

优化 Linux 性能:Linux 性能工具实践指南

- 有一节第 5 章:性能工具:特定于进程的内存 - 它提供了比您想要的更多的信息。


Gil*_*il' 5

ps为您提供每个进程使用的内存量。其中一些内存是 mmapped 文件,在缓存中计数。其中一些内存(尤其是代码)与其他进程共享,因此如果您将 RSS 值相加,它会被计算多次。

“这个进程使用了​​多少内存?”没有正确的答案,因为它不仅取决于进程,还取决于环境。有许多不同的值可以称为进程的“内存使用量”,它们不匹配或不相加,因为它们计算的是不同的东西。


小智 5

正如其他人正确指出的那样,很难掌握进程使用的实际内存、共享区域的内容以及映射文件等。

如果你是一个实验者,你可以运行valgrind 和 massif。这对于临时用户来说可能会有点繁重,但随着时间的推移,您会了解应用程序的内存行为。如果应用程序 malloc() 正是它所需要的,那么这将很好地表示进程的真实动态内存使用情况。但是这个实验是可以“中毒”的。

更复杂的是,Linux 允许您过度使用内存。当您 malloc() 内存时,您表示您打算消耗内存。但是分配不会真正发生,直到您将一个字节写入分配的“RAM”的新页面中。您可以通过编写和运行一个小 C 程序来证明这一点,如下所示:

// test.c
#include <malloc.h>
#include <stdio.h>
#include <unistd.h>
int main() {
    void *p;
    sleep(5)
    p = malloc(16ULL*1024*1024*1024);
    printf("p = %p\n", p);
    sleep(30);
    return 0;
}

# Shell:
cc test.c -o test && ./test &
top -p $!
Run Code Online (Sandbox Code Playgroud)

在 RAM 少于 16GB 的机器上运行它,瞧!您刚刚获得了 16GB 的内存!(不,不是真的)。

请注意,top您看到“VIRT”为 16.004G,但 %MEM 为 0.0

用 valgrind 再次运行:

# Shell:
valgrind --tool=massif ./test &
sleep 36
ms_print massif.out.$! | head -n 30
Run Code Online (Sandbox Code Playgroud)

并且 massif 说“所有 allocs() 的总和 = 16GB”。所以这不是很有趣。

但是,如果你在一个理智的进程上运行它:

# Shell:
rm test test.o
valgrind --tool=massif cc test.c -o test &
sleep 3
ms_print massif.out.$! | head -n 30

--------------------------------------------------------------------------------
Command:            cc test.c -o test
Massif arguments:   (none)
ms_print arguments: massif.out.23988
--------------------------------------------------------------------------------


    KB
77.33^                                                                       :
     |                                                                      #:
     |                                                                :@::@:#:
     |                                                           :::::@@::@:#:
     |                                                         @:: :::@@::@:#:
     |                                                     ::::@:: :::@@::@:#:
     |                                             ::@:::@:::::@:: :::@@::@:#:
     |                                            @::@:::@:::::@:: :::@@::@:#:
     |                                            @::@:::@:::::@:: :::@@::@:#:
     |                      :@@@@@@@@@@@@@@@@@@@@:@::@:::@:::::@:: :::@@::@:#:
     |                      :@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                    :@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                    :@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                   :@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |                   :@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |              :@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |          :::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |        :::::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |       ::::::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
     |       ::::::::@::::@@:@@                  :@::@:::@:::::@:: :::@@::@:#:
   0 +----------------------------------------------------------------------->Mi
     0                                                                   1.140
Run Code Online (Sandbox Code Playgroud)

在这里我们看到(非常有经验和非常高的信心)编译器分配了 77KB 的堆。

为什么要如此努力地获得堆使用?因为进程使用的所有共享对象和文本部分(在本例中为编译器)都不是很有趣。它们是一个过程的持续开销。事实上,该过程的后续调用几乎是“免费”的。

另外,比较和对比以下内容:

MMAP() 一个 1GB 的文件。您的 VMSize 将为 1+GB。但是您是驻留集大小将只是您导致被分页的文件部分(通过取消引用指向该区域的指针)。如果您“读取”整个文件,那么当您到达结尾时,内核可能已经将开头分页(这很容易做到,因为如果再次取消引用,内核确切地知道如何/在哪里替换这些页面) )。在任何一种情况下,VMSize 和 RSS 都不是您的内存“使用情况”的良好指标。你实际上还没有 malloc() 任何东西。

相比之下,Malloc() 和接触大量内存——直到你的内存被交换到磁盘。所以你分配的内存现在超过了你的 RSS。在这里,您的 VMSize 可能会开始告诉您一些事情(您的进程拥有的内存比实际驻留在 RAM 中的内存多)。但是仍然很难区分共享页面的虚拟机和交换数据的虚拟机。

这就是 valgrind/massif 变得有趣的地方。它向您展示了您有意分配的内容(无论您的页面状态如何)。