De que versão do kernel Linux / libc é Java Runtime.exec () segura em relação à memory?

No trabalho, uma das nossas plataformas de destino é um mini-servidor com resources restritos executando Linux (kernel 2.6.13, distribuição customizada baseada em um antigo Fedora Core). O aplicativo é escrito em Java (Sun JDK 1.6_04). O killer do Linux OOM é configurado para matar processos quando o uso de memory excede 160MB. Mesmo durante a carga alta, nosso aplicativo nunca ultrapassa 120 MB e, juntamente com alguns outros processos nativos que estão ativos, ficamos bem dentro do limite de OOM.

No entanto, verifica-se que o método Java Runtime.getRuntime (). Exec (), a maneira canônica de executar processos externos a partir de Java, tem uma implementação particularmente infeliz no Linux que faz com que processos filhos gerados (temporariamente) requeiram a mesma quantidade de memory como o processo pai desde que o espaço de endereço é copiado. O resultado líquido é que nosso aplicativo é morto pelo killer da OOM assim que executamos Runtime.getRuntime (). Exec ().

Atualmente, trabalhamos em torno disso tendo um programa nativo separado para executar todos os comandos externos e nos comunicamos com esse programa em um soquete. Isso é menos que ótimo.

Depois de postar sobre esse problema on-line , recebi alguns comentários indicando que isso não deveria ocorrer em versões “mais recentes” do Linux, já que eles implementam o método posix fork () usando copy-on-write, presumivelmente significando que ele copiará somente as páginas necessárias modificar quando for necessário, em vez de todo o espaço de endereço imediatamente.

Minhas perguntas são:

  • Isso é verdade?
  • Isso é algo no kernel, a implementação da libc ou em algum outro lugar inteiramente?
  • De qual versão do kernel / libc / o que é copy-on-write para fork () disponível?

Esse é basicamente o jeito que o * nix (e o linux) funcionou desde o início dos tempos (ou na madrugada de mmus).

Para criar um novo processo no * nixes você chama fork (). fork () cria uma cópia do processo de chamada com todos os seus mapeamentos de memory, descritores de arquivo, etc. Os mapeamentos de memory são feitos copy-on-write para que (em casos ideais) nenhuma memory seja realmente copiada, apenas os mapeamentos. Uma chamada exec () a seguir substitui o mapeamento de memory atual pelo do novo executável. Então, fork () / exec () é a maneira de criar um novo processo e é isso que a JVM usa.

A ressalva é que com processos enormes em um sistema ocupado, o pai pode continuar a executar por um tempo antes que o filho exec () faça com que uma quantidade enorme de memory seja copiada como causa da cópia na gravação. Em VMs, a memory pode ser movida muito para facilitar um coletor de lixo que produz ainda mais cópias.

A “solução” é fazer o que você já fez, criar um processo leve externo que cuida da geração de novos processos – ou usar uma abordagem mais leve do que fork / exec para gerar processos (que o linux não tem – e de qualquer maneira exigir uma mudança na própria jvm). Posix especifica a function posix_spawn (), que em teoria pode ser implementada sem copiar o mapeamento de memory do processo de chamada – mas no linux não é.

Bem, eu pessoalmente duvido que isso seja verdade, já que o fork () do Linux é feito via copy-on-write, já que Deus sabe quando (pelo menos, os kernels 2.2.x tinham, e era em algum lugar no 199x).

Uma vez que se acredita que o assassino da OOM seja um instrumento bruto que é conhecido por falhar (não é necessário matar o processo que realmente alocou a maior parte da memory) e que deve ser usado apenas como um último relatório, não está claro me porque você tem configurado para triggersr em 160M.

Se você quiser impor um limite na alocação de memory, então ulimit é seu amigo, não OOM.

Meu conselho é deixar a OOM sozinha (ou desativá-la completamente), configurar ulimits e esquecer esse problema.

Sim, este é absolutamente o caso até mesmo com novas versões do Linux (estamos no Red Hat 5.2 de 64 bits). Eu tenho tido um problema com subprocesss de execução lenta por cerca de 18 meses, e nunca consegui descobrir o problema até ler sua pergunta e executar um teste para verificá-lo.

Temos uma checkbox de 32 GB com 16 núcleos e, se executarmos a JVM com configurações como -Xms4g e -Xmx8g e executarmos subprocesss usando Runtime.exec () com 16 segmentos, não poderemos executar nosso processo mais rapidamente do que 20 processar chamadas por segundo.

Tente isso com o simples comando “date” no Linux cerca de 10.000 vezes. Se você adicionar o código de criação de perfil para observar o que está acontecendo, ele será iniciado rapidamente, mas diminuirá com o tempo.

Depois de ler sua pergunta, decidi tentar diminuir minhas configurações de memory para -Xms128m e -Xmx128m. Agora nosso processo é executado em cerca de 80 chamadas de processo por segundo. As configurações de memory da JVM foram todas alteradas.

Não parece estar sugando a memory de forma que fiquei sem memory, mesmo quando tentei com 32 threads. É apenas a memory extra que precisa ser alocada de alguma forma, o que causa um custo pesado de boot (e talvez de desligamento).

De qualquer forma, parece que deve haver uma configuração para desabilitar esse comportamento Linux ou talvez até mesmo na JVM.

1: sim. 2: Isso é dividido em duas etapas: Qualquer chamada de sistema como fork () é empacotada pelo glibc no kernel. A parte do kernel da chamada do sistema está no kernel / fork.c 3: Eu não sei. Mas eu aposto que seu kernel tem isso.

O assassino da OOM entra em ação quando a memory fraca é ameaçada em checkboxs de 32 bits. Eu nunca tive um problema com isso, mas existem maneiras de manter a OOM na baía. Esse problema pode ser algum problema de configuração do OOM.

Como você está usando um aplicativo Java, considere mudar para o Linux de 64 bits. Isso definitivamente deveria consertar isso. A maioria dos aplicativos de 32 bits pode ser executada em um kernel de 64 bits sem problemas, desde que as bibliotecas relevantes estejam instaladas.

Você também pode tentar o kernel PAE para o fedora de 32 bits.

    Intereting Posts