Existe uma maneira de diminuir o heap Java quando não estiver em uso?

Estou trabalhando em um aplicativo Java no momento e trabalhando para otimizar seu uso de memory. Estou seguindo as diretrizes para garbage collection adequada, tanto quanto sei. No entanto, parece que meu heap parece estar em seu tamanho máximo, mesmo que não seja necessário.

Meu programa executa uma tarefa intensiva de resources uma vez por hora, quando o computador não está em uso por uma pessoa. Essa tarefa usa um pedaço decente de memory, mas libera tudo imediatamente após a conclusão da tarefa. O criador de perfil do NetBeans revela que o uso da memory se parece com isso:

Uso de memória do programa Java

Eu realmente gostaria de dar todo esse espaço de heap de volta para o sistema operacional quando não estiver em uso. Não há razão para eu usar tudo isso enquanto o programa não vai fazer nada por pelo menos mais uma hora.

Isso é possível? Obrigado.

Você poderia, talvez, brincar com -XX:MaxHeapFreeRatio – essa é a porcentagem máxima (padrão 70) do heap que está livre antes de o GC encolher. Talvez configurá-lo um pouco menor (40 ou 50?) E, em seguida, usando System.gc() pode ir alguns comprimentos para obter o comportamento desejado?

Não há como forçar isso a acontecer, no entanto, você pode tentar encorajar a JVM a fazer isso, mas você não pode simplesmente arrancar a memory como e quando quiser. E enquanto o acima pode encolher o heap, essa memory não será necessariamente devolvida diretamente para o SO (embora em implementações recentes da JVM ele faça isso).

Versão curta: sim você pode.

Versão longa:

Como o Java / JVM gerencia a memory

Para a maioria dos aplicativos, os padrões da JVM estão bem. Parece que a JVM espera que os aplicativos sejam executados apenas por um período limitado. Portanto, não parece libertar a memory por conta própria.

Para ajudar a JVM a decidir como e quando executar a garbage collection, os seguintes parâmetros devem ser fornecidos:

  • -Xms Especifica o tamanho de heap mínimo
  • –Xmx Especifica o tamanho máximo do heap

Para aplicativos do servidor, inclua: -server

Isso não é suficiente para mim. Eu quero mais controle!

Caso os parâmetros mencionados acima não sejam suficientes, você pode influenciar o comportamento da JVM em relação à garbage collection.

Primeiro, você pode usar System.gc() para informar a VM quando acredita que a garbage collection faria sentido. E segundo você pode especificar qual dos coletores de lixo a JVM deve usar:

Diferentes tipos de coletores de lixo:

  • GC Serial

    Parâmetro da linha de comandos: -XX:+UseSerialGC

    Pára o seu aplicativo e executa o GC.

  • GC Paralelo

    Parâmetro da linha de comandos: -XX:+UseParallelGC -XX:ParallelGCThreads=value

    Executa coletas menores em paralelo com seu aplicativo. Reduz o tempo necessário para as principais collections , mas usa outro thread.

  • Compactação Paralela GC

    Parâmetro da linha de comandos: -XX:+UseParallelOldGC

    Executa collections importantes em paralelo com seu aplicativo. Usa mais resources de CPU, reduz o uso de memory.

  • CMS GC

    Parâmetro da linha de comandos: -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=value -XX:+UseCMSInitiatingOccupancyOnly

    Executa collections menores e com mais frequência que o Serial GC , limitando assim as quebras / paragens do aplicativo.

  • G1

    Parâmetro da linha de comando: -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC

    Experimental (pelo menos no Java 1.6): tenta garantir que o aplicativo nunca seja interrompido por mais de 1s.

    Exemplo

Uso de memory de um aplicativo da Web do Play Framework sem nenhuma otimização: Play Framework WebApp sem otimizações Como você pode ver, ele usa bastante espaço de heap e o espaço usado é liberado regularmente.

Neste caso, as otimizações com parâmetros apenas não foram efetivas. Havia algumas tarefas agendadas que usavam bastante memory. Nesse caso, o melhor desempenho foi obtido usando o CMS GC do CMS GC combinado com System.gc() após as operações intensivas de memory. Como resultado, o uso de memory do WebApp foi reduzido de 1,8 GB para cerca de 400 a 500 MB.

Você pode ver aqui outra captura de canvas do VisualVM que mostra como a memory é liberada pela JVM e, na verdade, retornada ao sistema operacional:

A memória está sendo liberada pela JVM e retornada ao sistema operacional Nota: Eu usei o botão “Perform GC” do VisualVM para executar o GC em vez de System.gc() no meu código, pois as tarefas agendadas que consomem a memory são lançadas apenas em horários específicos e um pouco mais difíceis de capturar com VisualVM .

Leitura Adicional

  • Artigo sobre ajuste do GC JVM
  • Documentação Oracle dos GCs disponíveis

Uma possibilidade é fazer com que seu aplicativo java em background inicie uma instância externa do jvm a cada hora para executar sua tarefa. Dessa forma, apenas seu aplicativo jvm original está em execução entre as tarefas.

A JVM não funciona dessa maneira. Você não pode devolvê-lo ao sistema operacional.

Como observado por várias pessoas desde que isso foi escrito, há quatro anos, você pode devolver a memory ao sistema operacional se fornecer as configurações corretas do GC para a JVM.

Se seu aplicativo ficar inativo durante os períodos de inatividade, é possível que o sistema operacional troque as páginas por você, reduzindo a pressão sobre a memory física.

http://www.linuxvox.com/2009/10/what-is-the-linux-kernel-parameter-vm-swappiness/

Java melhor mantido em segredo: -Xincgc Ele afeta o desempenho, mas nem sempre é muito. Às vezes, depende do que você está fazendo. O coletor de lixo incremental entrega a memory de volta ao sistema muito bem!