pl  |  en

Ile pamięci zajmuje ten proces?

Ile masz pamięci w komputerze? Laptop, na którym piszę te słowa, ma 2GB, serwer, na którym zaraz wylądują — 16GB. Na każdej z tych maszyn każdy uruchomiony proces może zaadresować 4GB, ani bajta mniej ani więcej[1]. Na wspomnianym laptopie działa właśnie około 200 procesów, co daje 800 gigabajtów widzianych łącznie przez procesy na maszynie dysponującej promilem tej wielkości. Jak to możliwe?

Proces jest ofiarą spisku procesora, na którym działa, i systemu operacyjnego. Wmawiają one procesowi, że jest sam w pamięci i ma do dyspozycji pełne cztery gigabajty. Dopiero w momencie kiedy próbuje z nich skorzystać, zastanawiają się co dalej. Zaczynamy od 4GB przestrzeni adresowej:

pamiec1

Jądro systemu operacyjnego[2] również potrzebuje przestrzeni adresowej. Najprostszym i najszybszym rozwiązaniem jest jej współdzielenie z procesem użytkownika. Zabierzmy mu więc ostatni gigabajt, w 3 GB też da się dużo zrobić:

pamiec2

Próba dostępu do ostatniego gigabajta skończy się przerwaniem działania procesu. Pozostałe trzy gigabajty to przestrzeń, w której proces może się rozgościć, ale zanim zacznie przechowywać w tej pamięci jakiekolwiek dane, musi to zadeklarować, tworząc obszary pamięci wirtualnej (VMA). Taki obszar może być powiązany z plikiem na dysku lub nie, może być prywatny lub dzielony między procesami i mieć jeszcze kilka innych, mniej lub bardziej bardziej ezoterycznych cech. Weźmy zupełnie losowo wybrany proces i sprawdźmy jego mapę pamięci:

$ pmap 28777
28777:   /usr/lib/chromium-browser/chromium-browser
00110000   1124K r-x--  /usr/lib/libX11.so.6.3.0
00229000      4K r----  /usr/lib/libX11.so.6.3.0
0022a000      8K rw---  /usr/lib/libX11.so.6.3.0
(...)
b4cac000     28K r--s-  /usr/lib/gconv/gconv-modules.cache
b4cb3000      4K r----  /usr/lib/locale/pl_PL.utf8/LC_IDENTIFICATION
b4cb4000      8K rw---    [ anon ]
b4cb6000  45080K r-x--  /usr/lib/chromium-browser/chromium-browser
b78bd000    872K r----  /usr/lib/chromium-browser/chromium-browser
b7997000     88K rw---  /usr/lib/chromium-browser/chromium-browser
b79ad000    340K rw---    [ anon ]
b8536000 119804K rw---    [ anon ]
bfca6000    112K rwx--    [ stack ]
bfcc2000      4K rw---    [ anon ]
 total   478952K

pamiec3

Pierwsza kolumna to adres, pod którym zaczyna się dany obszar pamięci wirtualnej, zapisany szesnastkowo. O przeliczaniu systemów liczbowych napisano już wystarczająco wiele, dlatego ograniczę się do stwierdzenia, że adres 0xbfcc2000 leży niecałe 4MB przed końcem trzeciego gigabajta. Nawet proces tak pamięciożerny jak współczesna przeglądarka nie wykorzystuje całych 3 GB dla siebie, zadowalając się „jedynie” 480 MB pamięci wirtualnej. Ale czy rzeczywiście wykorzystuje to pół gigabajta (często oznaczane jako VM) w całości?

$ grep VmRSS /proc/28777/status
VmRSS:    167968 kB

Daleko mu do tego — tylko 1/3 pamięci zadeklarowanej w rozmaitych VMA ma swoje podparcie w pamięci fizycznej maszyny. Ta wielkość nazywa się RSS od Resident Set Size. Zajrzyjmy głębiej pod maskę:

$ head -4 /proc/28777/smaps
00110000-00229000 r-xp 00000000 08:01 547235     /usr/lib/libX11.so.6.3.0
Size:               1124 kB
Rss:                 320 kB
Pss:                  40 kB

pamiec4

(to tylko wycinek danych dla pierwszego VMA z ponad 600, których używa obecnie moja przeglądarka, całości nie będę tu przytaczał). Z 1124 KB zadeklarowanych jako zajmowanych przez bibliotekę libX11 (Size) tylko 320 KB okazało się do tej pory potrzebne (RSS) i tylko tyle zostało załadowane z dysku. Resztę w razie potrzeby jądro załaduje w sposób całkowicie niewidoczny dla procesu (tylko pierwsze wywołanie tej nowej funkcji będzie wolniejsze). Podobnie obszary anonimowe (nie powiązane z konkretnymi plikami) są podpierane pamięcią fizyczną w miarę potrzeb. Ponieważ bardzo rzadko proces wykorzystuje tyle pamięci, ile zadeklarował, suma wielkości pamięci wirtualnych (VM) wszystkich procesów może wielokrotnie przekraczać rozmiar fizycznej pamięci maszyny bez negatywnych konsekwencji.

RSS to nie jedyna wielkość pozwalająca opisać fizyczne zużycie pamięci przez proces. Inną metryką jest PSS, czyli Proportional Set Size. Uwzględnia ona fakt, że wiele procesów może korzystać z tego samego obszaru (lub obszaru podpartego tym samym plikiem[3]) i dzielić pamięć fizyczną między sobą. Zsumujmy więc rozmiar PSS wszystkich obszarów naszego procesu[4]:

$ grep Pss /proc/28777/smaps | awk '{ sum += $2 } END { print sum }'
155401

Różnica w stosunku do RSS w przypadku tego procesu nie jest oszałamiająca, ale jest. Podsumowując, z półgigabajtowego potwora zrobił się 150MB… też w sumie potwór, ale jakby mniej straszny.

[1] A przynajmniej te 32-bitowe. Zasada działania dla procesów 64-bitowych jest bardzo podobna, tylko wielkości są mierzone w setkach terabajtów, w porywach do eksabajtów.

[2] 32-bitowy Linux z domyślnym ustawieniem VMSPLIT (3G/1G). Im więcej pamięci fizycznej, tym gorszy to jest wybór na serwer. Ale na komputerze osobistym się sprawdzi.

[3] Innymi słowy: jeżeli z danej biblioteki binarnej korzysta wiele procesów to w pamięci jest ona tylko raz. RSS tego nie uwzględnia, PSS tak.

[4] ‚grep’ jest tu teoretycznie zbędny ale filtrowanie awkiem dawało przekłamane wyniki. Hm.

Autor: Grzegorz Nosek

  • Magda Zarych

    W kolejnym poście napiszemy jak my rozliczamy pamięć, jaka jest różnica między RSS a PSS (to wliczamy do pamięci) dla poszczególnych typów aplikacji i jak przekłada się to na różnice pomiędzy naszą ofertą a typowym VPSem w innych firmach.

  • b4k3r

    Dobry wpis!