A seção 9.6 "Overcommit and OOM" no documento que @dunxd menciona é particularmente gráfica sobre os perigos de permitir supercomprometimento. No entanto, o 80
também me pareceu interessante, por isso fiz alguns testes.
O que eu descobri é que overcommit_ratio
afeta o total de RAM disponível para todos os processos. Os processos raiz não parecem ser tratados de forma diferente dos processos normais do usuário.
A definição da proporção para 100
ou menos deve fornecer a semântica clássica na qual os valores de retorno de malloc/sbrk
são confiáveis. A definição de proporções inferiores a 100
pode ser uma maneira de reservar mais RAM para atividades que não são de processo, como armazenamento em cache e assim por diante.
Portanto, no meu computador com 24 GiB de RAM, com a troca desativada, 9 GiB em uso, com top
mostrando
Mem: 24683652k total, 9207532k used, 15476120k free, 19668k buffers
Swap: 0k total, 0k used, 0k free, 241804k cached
Aqui estão algumas configurações de overcommit_ratio
e quanta RAM meu programa consumidor de RAM pode pegar (tocando em cada página) - em cada caso, o programa saiu limpo uma vez malloc
falhou.
50 ~680 MiB
60 ~2900 MiB
70 ~5200 MiB
100 ~12000 MiB
A execução de vários de uma só vez, mesmo com alguns como o usuário raiz, não alterou o valor total consumido juntos. É interessante que não foi possível consumir os últimos 3+ GiB ou mais; o free
não caiu muito abaixo do mostrado aqui:
Mem: 24683652k total, 20968212k used, 3715440k free, 20828k buffers
Os experimentos eram confusos - tudo o que usa malloc no momento em que toda RAM está em uso tende a travar, já que muitos programadores são péssimos em verificar falhas malloc em C, algumas bibliotecas populares ignoram-nas completamente, e C ++ e vários outros línguas são ainda piores.
A maioria das implementações iniciais de RAM imaginária que vi foi para lidar com um caso muito específico, onde um único processo grande - digamos 51% + de memória disponível - precisava de fork()
para exec()
algum programa de suporte, geralmente muito, muito menor. Sistemas operacionais com semântica copy-on-write permitiriam o fork()
, mas com a condição de que se o processo bifurcado realmente tentasse modificar muitas páginas de memória (cada uma delas teria que ser instanciada como uma nova página independente da inicial processo enorme) acabaria sendo morto. O processo pai estava apenas em perigo se alocando mais memória, e poderia lidar com o esgotamento, em alguns casos apenas esperando um pouco para que algum outro processo morra, e então continuando. O processo filho normalmente apenas se substituiu a um programa (normalmente menor) via exec()
e ficou livre da condição.
O conceito de supercomprometimento do Linux é uma abordagem extrema para permitir que ambos os fork()
ocorram, bem como para permitir que processos únicos sejam maciçamente sobrecarregados. As mortes causadas por OOM killer acontecem de forma assíncrona, até mesmo para programas que fazem a alocação de memória de maneira responsável. Eu pessoalmente odeio overcommit em todo o sistema em geral e o assassino em particular - ele promove uma abordagem de gerenciamento de memória que infecta bibliotecas e, através delas, todos os aplicativos que as utilizam.
Eu sugeriria configurar a proporção para 100, e ter uma partição swap também que geralmente só acabaria sendo usada por processos enormes - que geralmente usam apenas uma fração pequena da parte deles mesmos que é colocada em swap. e, assim, proteger a grande maioria dos processos do mau funcionamento do OOM killer. Isso deve manter seu servidor seguro contra a morte aleatória, e se ele foi escrito para lidar com malloc
com responsabilidade, até mesmo a salvo de se matar (mas não aposte no último).
Isso significa que estou usando isso em /etc/sysctl.d/10-no-overcommit.conf
vm.overcommit_memory = 2
vm.overcommit_ratio = 100