RSS è la quantità di memoria che questo processo ha attualmente nella memoria principale (RAM). VSZ è la quantità di memoria virtuale che il processo ha in totale. Ciò include tutti i tipi di memoria, sia nella RAM che scambiata. Questi numeri possono essere distorti perché includono anche librerie condivise e altri tipi di memoria. Puoi avere cinquecento istanze di bash
in esecuzione e la dimensione totale del loro footprint di memoria non sarà la somma dei loro valori RSS o VSZ.
Se hai bisogno di avere un'idea più dettagliata sull'impronta di memoria di un processo, hai alcune opzioni. Puoi passare attraverso /proc/$PID/map
ed elimina le cose che non ti piacciono. Se si tratta di librerie condivise, il calcolo potrebbe diventare complesso a seconda delle tue esigenze (che penso di ricordare).
Se ti interessa solo la dimensione dell'heap del processo, puoi sempre analizzare il [heap]
voce nel map
file. La dimensione che il kernel ha allocato per l'heap del processo può riflettere o meno il numero esatto di byte che il processo ha chiesto da assegnare. Ci sono minimi dettagli, interni del kernel e ottimizzazioni che possono eliminare tutto questo. In un mondo ideale, sarà tanto quanto il tuo processo richiede, arrotondato al multiplo più vicino della dimensione della pagina di sistema (getconf PAGESIZE
ti dirà di cosa si tratta — sui PC è probabilmente di 4.096 byte).
Se vuoi vedere quanta memoria ha allocato un processo , uno dei modi migliori è rinunciare alle metriche lato kernel. Invece, strumentizzi le funzioni di (de)allocazione della memoria heap della libreria C con LD_PRELOAD
meccanismo. Personalmente, abuso leggermente di valgrind
per ottenere informazioni su questo genere di cose. (Si noti che l'applicazione della strumentazione richiederà il riavvio del processo.)
Tieni presente che, poiché potresti anche eseguire il benchmarking dei tempi di esecuzione, valgrind
renderà i tuoi programmi leggermente più lenti (ma probabilmente entro le tue tolleranze).
Esempio eseguibile minimo
Affinché ciò abbia senso, devi comprendere le basi del paging:https://stackoverflow.com/questions/18431261/how-does-x86-paging-work e in particolare che il sistema operativo può allocare memoria virtuale tramite le tabelle delle pagine / il suo libro di memoria interno (memoria virtuale VSZ) prima che abbia effettivamente uno spazio di archiviazione su RAM o disco (memoria residente RSS).
Ora per osservarlo in azione, creiamo un programma che:
- alloca più RAM della nostra memoria fisica con
mmap
- scrive un byte su ogni pagina per garantire che ciascuna di queste pagine passi dalla memoria solo virtuale (VSZ) alla memoria effettivamente utilizzata (RSS)
- controlla l'utilizzo della memoria del processo con uno dei metodi menzionati in:https://stackoverflow.com/questions/1558402/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 a monte.
Compila ed esegui:
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
dove:
- 0x1000000000 ==64GiB:2 volte la RAM fisica del mio computer di 32GiB
- 0x200000000 ==8GiB:stampa la memoria ogni 8GiB, quindi dovremmo ottenere 4 stampe prima del crash a circa 32GiB
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
:richiesto per Linux per consentirci di effettuare una chiamata mmap più grande della RAM fisica:https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432
Uscita del programma:
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
Stato di uscita:
137
che per la regola del numero di segnale 128 + significa che abbiamo ottenuto il numero di segnale 9
, che man 7 signal
dice è SIGKILL, che viene inviato dal killer di memoria esaurita di Linux.
Interpretazione dell'output:
- La memoria virtuale VSZ rimane costante a
printf '0x%X\n' 0x40009A4 KiB ~= 64GiB
(ps
i valori sono in KiB) dopo mmap. - Il "vero utilizzo della memoria" RSS aumenta lentamente solo quando tocchiamo le pagine. Ad esempio:
- sulla prima stampa, abbiamo
extra_memory_committed 0
, il che significa che non abbiamo ancora toccato nessuna pagina. RSS è un piccolo1648 KiB
che è stato assegnato per il normale avvio del programma come area di testo, globali, ecc. - nella seconda stampa, abbiamo scritto a
8388608 KiB == 8GiB
valore di pagine. Di conseguenza, RSS è aumentato esattamente di 8 GIB a8390256 KiB == 8388608 KiB + 1648 KiB
- L'RSS continua ad aumentare con incrementi di 8GiB. L'ultima stampa mostra circa 24 GiB di memoria e prima che potessero essere stampati 32 GiB, il killer OOM ha interrotto il processo
- sulla prima stampa, abbiamo
Vedi anche:Hai bisogno di spiegazioni su Resident Set Size/Virtual Size
Registri OOM killer
Il nostro dmesg
i comandi hanno mostrato i log killer di OOM.
Un'esatta interpretazione di questi è stata chiesta a:
- https://stackoverflow.com/questions/9199731/understanding-the-linux-oom-killers-logs ma diamo una rapida occhiata qui.
- https://serverfault.com/questions/548736/how-to-read-oom-killer-syslog-messages
La primissima riga del registro era:
[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
Quindi vediamo che è stato interessante notare che è stato il demone MongoDB che viene sempre eseguito sul mio laptop in background a attivare per primo il killer OOM, presumibilmente quando il poveretto stava cercando di allocare un po' di memoria.
Tuttavia, il killer OOM non uccide necessariamente colui che lo ha svegliato.
Dopo l'invocazione, il kernel stampa una tabella o processi che includono 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
e più avanti vediamo che il nostro piccolo main.out
effettivamente è stato ucciso durante l'invocazione precedente:
[ 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
Questo registro menziona il score 865
che quel processo aveva, presumibilmente il più alto (peggiore) punteggio di OOM killer come menzionato in:In che modo l'OOM killer decide quale processo uccidere per primo?
Inoltre, è interessante notare che apparentemente tutto è accaduto così in fretta che prima che la memoria liberata fosse contabilizzata, il oom
è stato nuovamente svegliato dal DeadlineMonitor
processo:
[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
e questa volta questo ha ucciso un processo Chromium, che di solito è il normale consumo di memoria del mio computer:
[ 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
Testato in Ubuntu 19.04, kernel Linux 5.0.0.