Answers:
RSS是常驻集大小,用于显示有多少内存分配给该进程并且位于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中查看此信息。
要了解更多信息,请检查以下参考资料:
另请参阅:
libxml2.so
,则共享库将在其每个RSS中计数,因此其RSS的总和将超过所使用的实际内存。
top
命令。该系统没有任何交换,swapon --show
什么也不返回。您如何解释呢?如果vsz是swap +共享库,在这种情况下,共享库是否超过3.3G?可能吗?只是真的很困惑......
最小的可运行示例
为此,您必须了解分页的基础知识:x86分页如何工作?特别是OS可以在实际上在RAM或磁盘上有后备存储(RSS驻留内存)之前,通过页面表/它的内部内存簿记(VSZ虚拟内存)分配虚拟内存。
现在来看一下实际情况,让我们创建一个程序:
mmap
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;
/* /programming/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:
* /programming/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;
}
编译并运行:
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
哪里:
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
:Linux允许我们进行大于物理RAM的mmap调用所必需:malloc可以分配的最大内存程序输出:
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点+信号编号规则的手段,我们得到的信号数9
,这man 7 signal
说的是SIGKILL,这是由Linux发外的内存杀手。
输出解释:
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
(ps
值以KiB为单位)。extra_memory_committed 0
,这意味着我们还没有碰过任何页面。RSS很小1648 KiB
,已分配给正常的程序启动,如文本区域,全局变量等。8388608 KiB == 8GiB
一些页面。结果,RSS正好增加了8GIB,8390256 KiB == 8388608 KiB + 1648 KiB
另请参阅: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
该进程所具有的,可能是最高(最差)的OOM杀手得分,如以下网址所述:https : //unix.stackexchange.com/questions/153585/how-does-the-oom-killer-decide-which-首先杀人的过程
同样有趣的是,所有事情显然发生得如此之快,以至于在对释放的内存进行说明之前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中进行了测试。
我认为关于RSS与VSZ的讨论已经很多。从管理员/程序员/用户的角度来看,当我设计/编码应用程序时,我更加关注RSZ(常驻内存),并且当您不断拉出(堆积)越来越多的变量时,您会看到该值不断上升。尝试一个简单的程序来循环构建基于malloc的空间分配,并确保将数据填充到该malloc分配的空间中。RSS不断发展。就VSZ而言,它更多地是Linux进行的虚拟内存映射,其核心功能之一是从常规操作系统概念派生而来的。VSZ管理由内核的虚拟内存管理完成,有关VSZ的更多信息,请参见Robert Love对mm_struct和vm_struct的描述,它们是内核中基本task_struct数据结构的一部分。
它们不是受管的,而是经过度量的,并且可能受到限制(请参阅getrlimit
系统调用,以及getrlimit(2))。
RSS表示常驻集大小(虚拟地址空间中位于RAM中的部分)。
您可以查询虚拟地址空间使用过程1234 PROC(5)与cat /proc/1234/maps
它的地位(包括内存消耗)通cat /proc/1234/status