Ile pamięci zajmuje ten proces?
« Masz wiadomość? MOM | Konkurs »
6 Marzec 2011
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:
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ć:
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
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
(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