Rozdział 9.6 „Overcommit and OOM” w dokumencie, o którym wspomina @dunxd, jest szczególnie graficzny na temat niebezpieczeństw związanych z dopuszczeniem overcommit. Jednak 80
wyglądało to również dla mnie interesująco, więc przeprowadziłem kilka testów.
Odkryłem, że overcommit_ratio
wpływa na całkowitą pamięć RAM dostępną dla WSZYSTKICH procesów. Procesy rootowania nie wydają się być traktowane inaczej niż zwykłe procesy użytkownika.
Ustawienie współczynnika na 100
lub mniej powinno zapewnić klasyczną semantykę, w której wartości zwracane malloc/sbrk
są wiarygodne. Ustawienie niższych współczynników 100
może być sposobem na zarezerwowanie większej ilości pamięci RAM na działania niezwiązane z procesem, takie jak buforowanie i tak dalej.
Tak więc na moim komputerze z 24 GiB RAM, z wyłączoną zamianą, 9 GiB w użyciu, z top
wyświetlaniem
Mem: 24683652k total, 9207532k used, 15476120k free, 19668k buffers
Swap: 0k total, 0k used, 0k free, 241804k cached
Oto niektóre overcommit_ratio
ustawienia i ilość pamięci RAM, którą mój program RAM-konsument może pobrać (dotykając każdej strony) - w każdym przypadku program wyszedł poprawnie po malloc
awarii.
50 ~680 MiB
60 ~2900 MiB
70 ~5200 MiB
100 ~12000 MiB
Uruchamianie kilku jednocześnie, nawet z niektórymi użytkownikami root, nie zmieniło łącznej kwoty, którą zużyli razem. Interesujące jest to, że nie był w stanie zużyć ostatnich 3+ GiB; free
nie spadnie znacznie poniżej tego, co jest pokazane tutaj:
Mem: 24683652k total, 20968212k used, 3715440k free, 20828k buffers
Eksperymenty były chaotyczne - wszystko, co korzysta z malloc w chwili, gdy cała pamięć RAM jest w użyciu, ma tendencję do zawieszania się, ponieważ wielu programistów obawia się sprawdzania błędów malloc w C, niektóre popularne biblioteki kolekcji ignorują to całkowicie, a C ++ i różne inne języki są nawet gorzej.
Większość wczesnych implementacji wyobrażonej pamięci RAM dotyczyła bardzo konkretnego przypadku, w którym jeden duży proces - powiedzmy ponad 51% dostępnej pamięci - był potrzebny fork()
, aby exec()
jakiś program wsparcia, zwykle o wiele, znacznie mniejszy. Systemy operacyjne z semantyką kopiowania przy zapisie pozwoliłyby na to fork()
, ale pod warunkiem, że jeśli rozwidlony proces faktycznie spróbuje zmodyfikować zbyt wiele stron pamięci (każda z nich musiałaby zostać utworzona jako nowa strona niezależna od początkowego ogromnego procesu) w końcu zostanie zabity. Proces nadrzędny był zagrożony tylko przy przydzielaniu większej ilości pamięci i mógł poradzić sobie z wyczerpaniem, w niektórych przypadkach po prostu czekając trochę na śmierć innego procesu, a następnie kontynuując. Proces potomny zwykle po prostu zastępuje się (zwykle mniejszym) programem przezexec()
i wtedy był wolny od zastrzeżenia.
Nadmierna koncepcja Linuksa jest ekstremalnym podejściem pozwalającym zarówno na fork()
wystąpienie, jak i na masową ogólną alokację pojedynczych procesów. Zgony OOM-killer-spowodowane zdarzyć asynchronicznie, nawet do programów, które zrobienia alokacji pamięci uchwyt odpowiedzialnie. Ja osobiście nienawidzę nadmiernego zaangażowania całego systemu, a szczególnie zabójcy zagłady - sprzyja to diabelnie ostrożnemu podejściu do zarządzania pamięcią, które infekuje biblioteki i za ich pośrednictwem każdą aplikację, która ich używa.
Sugeruję ustawienie współczynnika na 100, a także posiadanie partycji wymiany, która na ogół kończy się przyzwyczajeniem się przez ogromne procesy - które często wykorzystują tylko niewielką część siebie, która zostaje upchnięta w swap, a zatem chronić ogromną większość procesów przed błędami zabójcy OOM. To powinno zabezpieczyć twój serwer przed przypadkową śmiercią, a jeśli został napisany z myślą o malloc
odpowiedzialnym postępowaniu , nawet bezpieczny przed samobójstwem (ale nie stawiaj na to drugie).
To znaczy, że używam tego w /etc/sysctl.d/10-no-overcommit.conf
vm.overcommit_memory = 2
vm.overcommit_ratio = 100