A Seção 9.6 "Confirmação excessiva e OOM" no documento mencionado pelo @dunxd é particularmente gráfica sobre os perigos de permitir a confirmação excessiva. No entanto, 80
parecia interessante para mim, então realizei alguns testes.
O que descobri é que isso overcommit_ratio
afeta a RAM total disponível para TODOS os processos. Os processos raiz não parecem ser tratados de maneira diferente dos processos normais do usuário.
Definir a proporção como 100
ou menos deve fornecer a semântica clássica da qual os valores de retorno malloc/sbrk
são confiáveis. Definir taxas mais baixas do que 100
pode ser uma maneira de reservar mais RAM para atividades não relacionadas ao processo, como cache e assim por diante.
Então, no meu computador com 24 GiB de RAM, com swap desativado, 9 GiB em uso, top
mostrando
Mem: 24683652k total, 9207532k used, 15476120k free, 19668k buffers
Swap: 0k total, 0k used, 0k free, 241804k cached
Aqui estão algumas overcommit_ratio
configurações e quanta RAM meu programa consumidor de memória ram poderia pegar (tocando em cada página) - em cada caso, o programa saiu de forma limpa uma vez que 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 usuário root, não alterou a quantidade total que consumiram juntos. É interessante que não foi possível consumir os últimos 3 ou mais GiB; o free
não caiu muito abaixo do que é mostrado aqui:
Mem: 24683652k total, 20968212k used, 3715440k free, 20828k buffers
Os experimentos foram confusos - qualquer coisa que use malloc no momento em que toda a RAM está em uso tende a falhar, já que muitos programadores são péssimos em verificar falhas de malloc em C, algumas bibliotecas populares de coleções o ignoram completamente e C ++ e várias outras linguagens são até pior.
A maioria das implementações iniciais da RAM imaginária que vi foram para lidar com um caso muito específico, em que um único processo grande - digamos 51% + de memória disponível - era necessário fork()
para exec()
um programa de suporte, geralmente um muito, muito menor. Sistemas operacionais com semântica de copiar na gravação permitiriam fork()
, mas com a condição de que, se o processo bifurcado realmente tentasse modificar muitas páginas de memória (cada uma das quais teria que ser instanciada como uma nova página independente do enorme processo inicial) acabaria sendo morto. O processo pai só estava em perigo se alocasse mais memória e poderia se esgotar, em alguns casos, apenas esperando um pouco para que outro processo morresse e continuando. O processo filho geralmente apenas se substitui por um programa (geralmente menor) viaexec()
e foi então livre da condição.
O conceito de supercomprometimento do Linux é uma abordagem extrema para permitir que ambos fork()
ocorram, bem como permitir que processos únicos sejam generalizados em massa. Mortes OOM-causou-assassinas acontecer de forma assíncrona, até programas que fazer alocação de memória alça de forma responsável. Pessoalmente, odeio o comprometimento excessivo em todo o sistema em geral e o assassino em particular - ele promove uma abordagem demolidora para o gerenciamento de memória que infecta bibliotecas e, por meio deles, todos os aplicativos que os usam.
Eu sugeriria definir a proporção para 100 e ter uma partição de troca que geralmente acabaria sendo usada por processos enormes - que geralmente usam apenas uma pequena fração da parte de si que é armazenada na troca e, portanto, proteja a grande maioria dos processos das falhas de funcionalidade do OOM killer. Isso deve manter seu servidor da web protegido contra morte aleatória e, se ele foi escrito para lidar com malloc
responsabilidade, pode até se matar (mas não aposte nesse último).
Isso significa que estou usando isso no /etc/sysctl.d/10-no-overcommit.conf
vm.overcommit_memory = 2
vm.overcommit_ratio = 100