什么是Linux内存管理中的RSS和VSZ

时间:2011-10-24 19:23:47

标签: linux

Linux内存管理中的RSS和VSZ是什么?在多线程环境中,如何管理和跟踪这两者?

6 个答案:

答案 0 :(得分:402)

RSS是Resident Set Size,用于显示分配给该进程并在RAM中的内存量。它不包括换出的内存。它确实包括来自共享库的内存,只要这些库中的页面实际上在内存中。它确实包括所有堆栈和堆内存。

VSZ是虚拟内存大小。它包括进程可以访问的所有内存,包括被换出的内存,已分配但未使用的内存以及来自共享库的内存。

因此,如果进程A具有500K二进制文件并且链接到2500K共享库,则具有200K的堆栈/堆分配,其中100K实际上在内存中(其余是交换或未使用),并且它实际上只加载了1000K的共享库和400K自己的二进制文件然后:

RSS: 400K + 1000K + 100K = 1500K
VSZ: 500K + 2500K + 200K = 3200K

由于部分内存是共享的,因此许多进程可能会使用它,因此如果您添加所有RSS值,您可以轻松获得比系统更多的空间。

在程序实际使用之前,分配的内存也可能不在RSS中。因此,如果您的程序预先分配了一堆内存,然后随着时间的推移使用它,您可以看到RSS上升,VSZ保持不变。

还有PSS(比例设定大小)。这是一种较新的度量,它将共享内存跟踪为当前进程使用的比例。因此,如果有两个进程使用之前的相同共享库:

PSS: 400K + (1000K/2) + 100K = 400K + 500K + 100K = 1000K

线程都共享相同的地址空间,因此每个线程的RSS,VSZ和PSS与进程中的所有其他线程相同。使用ps或top在linux / unix中查看此信息。

除此之外还有更多内容,要了解更多内容,请查看以下参考资料:

另见:

答案 1 :(得分:43)

RSS是驻留集大小(物理驻留内存 - 这当前占用机器物理内存中的空间),VSZ是虚拟内存大小(分配地址空间 - 这个地址分配在进程的内存映射中,但是没有'现在一切都是背后的任何实际记忆。)

请注意,在普通虚拟机的这些日子里,机器视点的物理内存可能并不是真正的物理内存。

答案 2 :(得分:6)

我认为已经说过很多关于RSS vs VSZ的内容了。从管理员/程序员/用户的角度来看,当我设计/编写应用程序时,我更关心RSZ(驻留内存),当你不断拉动越来越多的变量(堆积)时,你会看到这个值上升。尝试一个简单的程序在循环中构建基于malloc的空间分配,并确保在malloc空间中填充数据。 RSS不断向上发展。 就VSZ而言,它更多的是Linux所做的虚拟内存映射,其核心功能之一来自传统的操作系统概念。 VSZ管理由内核的虚拟内存管理完成,有关VSZ的更多信息,请参阅Robert Love关于mm_struct和vm_struct的描述,这些内容是内核中基本task_struct数据结构的一部分。

答案 3 :(得分:5)

最小的可运行示例

为此,您必须了解分页的基本知识:How does x86 paging work?,尤其是操作系统可以在实际使用之前通过页表/内部内存簿(VSZ虚拟内存)分配虚拟内存。在RAM或磁盘(RSS驻留内存)上具有后备存储。

现在要观察这一情况,让我们创建一个程序:

  • 使用mmap分配的RAM大于我们的物理内存
  • 在每个页面上写入一个字节,以确保每个页面从仅虚拟内存(VSZ)变为实际使用的内存(RSS)
  • 使用Memory usage of current process in C
  • 中提到的方法之一检查进程的内存使用情况

main.c

#define _GNU_SOURCE
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

typedef struct {
    unsigned long size,resident,share,text,lib,data,dt;
} ProcStatm;

/* https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c/7212248#7212248 */
void ProcStat_init(ProcStatm *result) {
    const char* statm_path = "/proc/self/statm";
    FILE *f = fopen(statm_path, "r");
    if(!f) {
        perror(statm_path);
        abort();
    }
    if(7 != fscanf(
        f,
        "%lu %lu %lu %lu %lu %lu %lu",
        &(result->size),
        &(result->resident),
        &(result->share),
        &(result->text),
        &(result->lib),
        &(result->data),
        &(result->dt)
    )) {
        perror(statm_path);
        abort();
    }
    fclose(f);
}

int main(int argc, char **argv) {
    ProcStatm proc_statm;
    char *base, *p;
    char system_cmd[1024];
    long page_size;
    size_t i, nbytes, print_interval, bytes_since_last_print;
    int snprintf_return;

    /* Decide how many ints to allocate. */
    if (argc < 2) {
        nbytes = 0x10000;
    } else {
        nbytes = strtoull(argv[1], NULL, 0);
    }
    if (argc < 3) {
        print_interval = 0x1000;
    } else {
        print_interval = strtoull(argv[2], NULL, 0);
    }
    page_size = sysconf(_SC_PAGESIZE);

    /* Allocate the memory. */
    base = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
    );
    if (base == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Write to all the allocated pages. */
    i = 0;
    p = base;
    bytes_since_last_print = 0;
    /* Produce the ps command that lists only our VSZ and RSS. */
    snprintf_return = snprintf(
        system_cmd,
        sizeof(system_cmd),
        "ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == \"%ju\") print}'",
        (uintmax_t)getpid()
    );
    assert(snprintf_return >= 0);
    assert((size_t)snprintf_return < sizeof(system_cmd));
    bytes_since_last_print = print_interval;
    do {
        /* Modify a byte in the page. */
        *p = i;
        p += page_size;
        bytes_since_last_print += page_size;
        /* Print process memory usage every print_interval bytes.
         * We count memory using a few techniques from:
         * https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c */
        if (bytes_since_last_print > print_interval) {
            bytes_since_last_print -= print_interval;
            printf("extra_memory_committed %lu KiB\n", (i * page_size) / 1024);
            ProcStat_init(&proc_statm);
            /* Check /proc/self/statm */
            printf(
                "/proc/self/statm size resident %lu %lu KiB\n",
                (proc_statm.size * page_size) / 1024,
                (proc_statm.resident * page_size) / 1024
            );
            /* Check ps. */
            puts(system_cmd);
            system(system_cmd);
            puts("");
        }
        i++;
    } while (p < base + nbytes);

    /* Cleanup. */
    munmap(base, nbytes);
    return EXIT_SUCCESS;
}

GitHub upstream

编译并运行:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
sudo dmesg -c
./main.out 0x1000000000 0x200000000
echo $?
sudo dmesg

其中:

  • 0x1000000000 == 64GiB:2倍于我的计算机的32GiB物理RAM
  • 0x200000000 == 8GiB:每8GiB打印一次内存,因此我们应该在崩溃之前在32GiB左右获得4张打印纸
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory:Linux所必需,它使我们能够进行大于物理RAM的mmap调用:maximum memory which malloc can allocate

程序输出:

extra_memory_committed 0 KiB
/proc/self/statm size resident 67111332 768 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 1648

extra_memory_committed 8388608 KiB
/proc/self/statm size resident 67111332 8390244 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 8390256

extra_memory_committed 16777216 KiB
/proc/self/statm size resident 67111332 16778852 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 16778864

extra_memory_committed 25165824 KiB
/proc/self/statm size resident 67111332 25167460 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 25167472

Killed

退出状态:

137

128 + signal number rule表示我们得到了信号号9man 7 signal说的是SIGKILL,它是由Linux out-of-memory killer发送的。

输出解释:

  • 在mmap之后,VSZ虚拟内存保持不变,printf '0x%X\n' 0x40009A4 KiB ~= 64GiBps的值在KiB中)。
  • 仅当我们触摸页面时,RSS“实际内存使用量”才缓慢增加。例如:
    • 在第一张纸上,我们有extra_memory_committed 0,这意味着我们还没有触摸过任何页面。 RSS是一个小型1648 KiB,已分配给普通程序启动,例如文本区域,全局变量等。
    • 在第二张纸上,我们已写入了8388608 KiB == 8GiB页。结果,RSS精确增加了8GIB,达到8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS继续以8GiB增量增加。最后的打印显示了大约24 GiB的内存,并且在可以打印32 GiB之前,OOM杀手杀死了该进程

另请参阅:https://unix.stackexchange.com/questions/35129/need-explanation-on-resident-set-size-virtual-size

OOM杀手日志

我们的dmesg命令显示了OOM杀手日志。

在以下位置要求对它们进行准确的解释

日志的第一行是:

[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

所以有趣的是,MongoDB守护程序始终在我的笔记本电脑中始终在后台运行,这首先触发了OOM杀手,大概是在可怜的事情试图分配内存的时候。

但是,OOM​​杀手并不一定要杀死唤醒它的人。

调用后,内核将打印一个包含oom_score的表或进程:

[ 7283.479292] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
[ 7283.479303] [    496]     0   496    16126        6   172032      484             0 systemd-journal
[ 7283.479306] [    505]     0   505     1309        0    45056       52             0 blkmapd
[ 7283.479309] [    513]     0   513    19757        0    57344       55             0 lvmetad
[ 7283.479312] [    516]     0   516     4681        1    61440      444         -1000 systemd-udevd

再往前,我们看到我们自己的小main.out实际上在上一次调用时被杀死了:

[ 7283.479871] Out of memory: Kill process 15665 (main.out) score 865 or sacrifice child
[ 7283.479879] Killed process 15665 (main.out) total-vm:67111332kB, anon-rss:92kB, file-rss:4kB, shmem-rss:30080832kB
[ 7283.479951] oom_reaper: reaped process 15665 (main.out), now anon-rss:0kB, file-rss:0kB, shmem-rss:30080832kB

此日志提及该过程具有的score 865,据推测是https://unix.stackexchange.com/questions/153585/how-does-the-oom-killer-decide-which-process-to-kill-first

中OOM杀手得分最高(最差)的原因。

有趣的是,所有事情显然发生得如此之快,以至于在计算释放的内存之前,oom流程再次唤醒了DeadlineMonitor

[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

这一次杀死了一些Chromium进程,这通常是我计算机的正常内存消耗:

[ 7283.481773] Out of memory: Kill process 11786 (chromium-browse) score 306 or sacrifice child
[ 7283.481833] Killed process 11786 (chromium-browse) total-vm:1813576kB, anon-rss:208804kB, file-rss:0kB, shmem-rss:8380kB
[ 7283.497847] oom_reaper: reaped process 11786 (chromium-browse), now anon-rss:0kB, file-rss:0kB, shmem-rss:8044kB

在Ubuntu 19.04,Linux内核5.0.0中进行了测试。

答案 4 :(得分:3)

VSZ - 虚拟集大小

  • 虚拟集大小是在初始执行期间分配给进程(程序)的内存大小。虚拟集大小内存只是进程可用于执行的内存数量。

RSS - 常驻集大小

  • 与 VSZ(虚拟集大小)相反,RSS 是进程当前使用的内存。这是当前进程使用的 RAM 量的实际数字(以千字节为单位)。

Source

答案 5 :(得分:1)

它们不受管理,但是经过测量并且可能有限(请参阅getrlimit系统调用,也在getrlimit(2)上)。

RSS表示resident set size(虚拟地址空间中位于RAM中的部分)。

您可以使用virtual address space使用cat /proc/1234/maps查询流程1234的proc(5)及其状态(包括内存消耗)至cat /proc/1234/status