But isn't it crazy how we throw out so much memory just because of random buffers? It feels wrong to me
There's a common noob complaint about "Linux using all my RAM!" where people are confused about the headline free/buffers numbers. If there's a reasonable chance data could be used again soon it's better to leave it in RAM; if the RAM is needed for something else, the current contents will get paged out. Having a chunk of RAM be genuinely unallocated to anything is doing nothing for you.
Your perspective sounds like the concept that space in a room does nothing for you until/unless you cram it full of hoarded items.
Why would anyone buy a locker 5x the size of their needs ?
Run vi/nano/micro/joe - they're optimizing for memory to some extent. vi clocks in at under 8 MB. You're giving up a lot of "nice" things to get there.
The portions that are allocated but not yet used might just be page table entries with no backing memory, making them free. Except for the memory tracking the page table entries. Almost free....
A lot of "image" will be mmapped and clean. Anything you don't actually use from that will be similarly freeish. Anything that's constantly needed will use memory. Except if it's mapped into multiple processes, then it's needed but responsibility is spread out. How do you count an app's memory usage when there's a big chunk of code that needs to sit in RAM as long as any of a dozen processes are running? How do you count code that might be used sometime in the next few minutes or might not be depending on what the user does?
ASLR is not an obstacle -- the same exact code can be mapped into different base addresses in different processes, so they can be backed by the same actual memory.
root@1f0775a74fd7:/tmp# cat a.c
int glob;
int main() {
return glob;
}
root@1f0775a74fd7:/tmp# gcc -c a.c -fPIC -o a.o
root@1f0775a74fd7:/tmp# objdump --disassemble=main a.o
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # f <main+0xf>
f: 8b 00 mov (%rax),%eax
11: 5d pop %rbp
12: c3 ret
root@1f0775a74fd7:/tmp# readelf -rW a.o | grep glob
000000000000000b 000000030000002a R_X86_64_REX_GOTPCRELX 0000000000000000 glob - 4
root@1f0775a74fd7:/tmp# gcc -shared -o a.so a.o
root@1f0775a74fd7:/tmp# objdump --disassemble=main a.so
(...)
00000000000010f9 <main>:
10f9: f3 0f 1e fa endbr64
10fd: 55 push %rbp
10fe: 48 89 e5 mov %rsp,%rbp
1101: 48 8b 05 b8 2e 00 00 mov 0x2eb8(%rip),%rax # 3fc0 <glob-0x4c>
1108: 8b 00 mov (%rax),%eax
110a: 5d pop %rbp
110b: c3 ret
(...)
root@1f0775a74fd7:/tmp# readelf -r a.so | grep glob
000000003fc0 000600000006 R_X86_64_GLOB_DAT 000000000000400c glob + 0
root@1f0775a74fd7:/tmp# gcc -shared -Wl,-Bsymbolic -o a.symb.so a.o
root@1f0775a74fd7:/tmp# readelf -r a.symb.so | grep glob
root@1f0775a74fd7:/tmp# objdump --disassemble=main a.symb.so
(...)
Disassembly of section .text:
00000000000010f9 <main>:
10f9: f3 0f 1e fa endbr64
10fd: 55 push %rbp
10fe: 48 89 e5 mov %rsp,%rbp
1101: 48 8d 05 04 2f 00 00 lea 0x2f04(%rip),%rax # 400c <glob>
1108: 8b 00 mov (%rax),%eax
110a: 5d pop %rbp
110b: c3 ret
(...)Global variables in PIC shared libraries are really weird: the shared library's variable is placed into the main program image data segment and the relocation is happening in the shared library, which means that there is an indirection generated in the library's machine code.
This optimization is essential. A typical process maps in hundreds of megabytes of code from the OS. There are hundreds of processes running at any given time. Eyeballing the numbers on an older Mac I have here (a newer one would surely be worse) I'd need maybe 50GB of RAM just to hold the code of all the running processes if the pages couldn't be shared.
- 100MB 'image' (ie executable code; the executable itself plus all the OS libraries loaded.)
- 40MB heap
- 50MB "mapped file", mostly fonts opened with mmap() or the windows equivalent
- 45MB stack (each thread gets 2MB)
- 40MB "shareable" (no idea)
- 5MB "unusable" (appears to be address space that's not usable because of fragmentation, not actual RAM)
Generally if something's using a lot of RAM, the answer will be bitmaps of various sorts: draw buffers, decompressed textures, fonts, other graphical assets, and so on. In this case it's just allocated but not yet used heap+stacks, plus 100MB for the code.
Edit: I may be underestimating the role of binary code size. Visual Studio "devenv.exe" is sitting at 2GB of 'image'. Zoom is 500MB. VSCode is 300MB. Much of which are app-specific, not just Windows DLLs.