Linux memory demystified
snapshot of “/proc/{$process}/status”
VmPeak: 1598724 kB
VmSize: 1598724 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 187852 kB
VmRSS: 187852 kB
RssAnon: 79612 kB
RssFile: 102264 kB
RssShmem: 5976 kB
VmData: 813488 kB
VmStk: 136 kB
VmExe: 83444 kB
VmLib: 40380 kB
https://ewx.livejournal.com/579283.html
VmPeak: Peak “address space” used.
VmSize: Current “address space” in use.
VmHWM: High Water Mark of “physical RAM”
VmRSS: Current usage of “physical RAM”
VmData: VmSize minus Size of (shared pages and stack pages)
VmStk: the size of the stack of the “initial thread” in the process
#include <stdio.h>
#include <new>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>#define ALLOC_SIZE 4*1024*1024
unsigned char* pool[1024*1024*4];
unsigned long long size = 0;void printProc(const char* description)
{
char tmp[513];
sprintf(tmp, "/proc/%d/status", getpid());
if (FILE *fstat = fopen(tmp, "r")) {
const int r = fread(tmp, 1, 512, fstat);
if (r > 0) {
tmp[r] = '\0';
printf("%s ==================\n", description);
printf("%s\n", tmp);
} else {
printf("Failed to read from %s", tmp);
}
fclose(fstat);
}
}int main()
{
int i = 0;
while (true) {
unsigned char* pool_
= static_cast<unsigned char*>(mmap(nullptr,
16*1024*1024,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0)); if (pool_ == nullptr ||
pool_ == reinterpret_cast<void*>(MAP_FAILED) ) {
printProc("mmap failed");
close();
assert(0); // want to know what caused MAP_FAILED
}
}
}
This will result in
errno:12
Umask: 0002
State: R (running)
Tgid: 9548
Ngid: 0
Pid: 9548
PPid: 5393
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
Groups: 4 24 27 30 46 113 128 1000
NStgid: 9548
NSpid: 9548
NSpgid: 9548
NSsid: 5393
VmPeak: 4151256 kB
VmSize: 4151256 kB
VmLck: 68 kB
VmPin: 0 kB
VmHWM: 175144 kB
VmRSS: 175144 kB
RssAnon: 79196 kB
RssFile: 91200 kB
RssShmem: 4748 kB
VmData: 3929320 kB
VmStk: 136 kB
VmExe: 57488 kB
VmLib: 43780 kB
VmPTE
It failed with errno 12 which means cannot allocate memory. Here, the reason is that it depleted all virtual memory address space with `VmSize: 4151256 kB`
When filling the memory with random values
int i = 0;
while (true) {
pool_ = static_cast<unsigned char*>(mmap(nullptr,
16*1024*1024,
PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0));if (pool_ == nullptr ||
pool_ == reinterpret_cast<void*>(MAP_FAILED) ) {
printProc("mmap failed");
close();
assert(0); // want to know what caused MAP_FAILED
} else {
RAND_bytes(pool_, 16*1024*1024);
}
still, we fail when there is no virtual address space left.
Umask: 0002
State: R (running)
Tgid: 12279
Ngid: 0
Pid: 12279
PPid: 5393
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 256
Groups: 4 24 27 30 46 113 128 1000
NStgid: 12279
NSpid: 12279
NSpgid: 12279
NSsid: 5393
VmPeak: 4152612 kB
VmSize: 4152612 kB
VmLck: 68 kB
VmPin: 0 kB
VmHWM: 3502280 kB
VmRSS: 3502280 kB
RssAnon: 3405460 kB
RssFile: 91096 kB
RssShmem: 5724 kB
VmData: 3929764 kB
VmStk: 136 kB
VmExe: 57488 kB
VmLib: 43780 kB
Explanation from “man top”
Linux Memory Types
For our purposes, there are three types of memory, and one is optional.
- physical memory: a limited resource where code and data must reside when executed or referenced.
- (optional) swap file: where modified (dirty) memory can be saved and later retrieved if too many demands are made on physical memory.
- virtual memory: a nearly unlimited resource serving the following goals:
1. abstraction, free from physical memory addresses/limits
2. isolation, every process in a separate address space
3. sharing, a single mapping can serve multiple needs
4. flexibility, assign a virtual address to a file
Regardless of which of these forms memory may take, all are managed as pages (typically 4096 bytes) but expressed by default in ‘top’ as KiB (kibibyte).
$ getconf PAGESIZE
4096
For each such process, every memory page is restricted to a single quadrant from the table below.
Both physical memory and virtual memory can include any of the four, while the swap file only includes #1 through #3.
The memory in quadrant #4, when modified, acts as its own dedicated swap file.
The following may help in interpreting process-level memory values displayed as scalable columns.
- %MEM — simply RES divided by total physical memory.
- CODE — the `pgms’ portion of quadrant 3.
- DATA — the entire quadrant 1 portion of VIRT plus all explicit mmap file-backed pages of quadrant 3.
- RES — anything occupying physical memory which, beginning with Linux-4.5, is the sum of the following three fields:
RSan — quadrant 1 pages, which include any former quadrant 3 pages if modified
RSfd — quadrant 3 and quadrant 4 pages
RSsh — quadrant 2 pages - RSlk — subset of RES which cannot be swapped out (any quadrant)
- SHR — subset of RES (excludes 1, includes all 2 & 4, some 3)
- SWAP — potentially any quadrant except 4
- USED — simply the sum of RES and SWAP
- VIRT — everything in-use and/or reserved (all quadrants)
Note: Even though program images and shared libraries are considered private to a process, they will be accounted for as shared (SHR) by the kernel.