Como liberar memória em Java?

146

Existe uma maneira de liberar memória em Java, semelhante à free()função de C ? Ou definir o objeto como nulo e confiar na GC é a única opção?

Felix
fonte
151
Ok ... vamos esclarecer uma coisa. Só porque você acha que algo é uma prática ruim e não é algo que incentive a fazer, não o torna digno de uma votação. Esta é uma pergunta clara e válida, perguntando se existe uma maneira de liberar memória em Java sem depender da coleta de lixo. Embora possa ser desencorajado e geralmente não seja útil ou seja uma boa ideia, você não pode saber que não há cenários em que isso possa ser necessário sem saber o que Felix sabe. Felix pode nem estar planejando usá-lo. Ele pode apenas querer saber se é possível. De forma alguma, merece uma votação.
21411 Daniel Bingham
7
Para esclarecimento, é destinado a quem votou contra - não necessariamente comentários anteriores.
21411 Daniel Bingham

Respostas:

96

Java usa memória gerenciada; portanto, a única maneira de alocar memória é usando o newoperador, e a única maneira de desalocar memória é contando com o coletor de lixo.

Este white paper de gerenciamento de memória (PDF) pode ajudar a explicar o que está acontecendo.

Você também pode ligar System.gc()para sugerir que o coletor de lixo seja executado imediatamente. No entanto, o Java Runtime toma a decisão final, não o seu código.

De acordo com a documentação Java ,

Chamar o método gc sugere que a Java Virtual Machine dedique esforços para reciclar objetos não utilizados, a fim de disponibilizar a memória que eles ocupam atualmente para reutilização rápida. Quando o controle retorna da chamada de método, a Java Virtual Machine se esforça ao máximo para recuperar o espaço de todos os objetos descartados.

Daniel Pryden
fonte
5
Isso força o Garbage Collector a executar. Não forçá-lo a memória livre embora ...
Pablo Santa Cruz
13
Sem Pablo, ele não força o GC a funcionar.
Jesper
1
Foi-me dito por uma pessoa muito confiável que todos os coletores de lixo do HotSpotVM ignoram System.gc()completamente.
Esko
1
No winXp java, o SE GC executa todos os System.gc () ou quase todos, mas o documento da API não garante isso.
Teodósio
2
@ Pablo Santa Cruz Como assim, não libera memória? Acabei de testar no meu programa que parecia ter um vazamento e o uso da memória ram parecia estabilizar? E Daniel acabou de dizer que apenas sugere isso, então como é que a porcentagem de RAM usada sempre se estabilizou cada vez que chamei o método. Vocês estão me confundindo.
65

Parece que ninguém mencionou a definição explícita de referências a objetos null, que é uma técnica legítima para "liberar" a memória que você pode querer considerar.

Por exemplo, digamos que você tenha declarado um List<String>no início de um método que cresceu em tamanho para ser muito grande, mas só foi necessário até a metade do método. Nesse ponto, você pode definir a referência de lista nullpara permitir que o coletor de lixo potencialmente recupere esse objeto antes que o método seja concluído (e a referência fica fora do escopo de qualquer maneira).

Observe que raramente uso essa técnica na realidade, mas vale a pena considerar ao lidar com estruturas de dados muito grandes.

Adamski
fonte
8
Se você realmente está trabalhando muito em um objeto que é usado apenas para parte de um método, sugiro; seu método é muito compilicated, quebrar o método para o antes e depois de porções, ou usar um bloco para o primeiro semestre de código (o mais tarde é mais útil para scripts de teste)
Peter Lawrey
5
O local em que a definição de uma referência de objeto como nulo é importante é quando é referenciada a partir de outro objeto de longa duração (ou possivelmente de uma var estática). Por exemplo, se você tem uma matriz de objetos grandes de longa duração e deixa de usar um desses objetos, deve definir a referência da matriz como nula para disponibilizar o objeto para o GC.
Hot Licks
22
System.gc(); 

Executa o coletor de lixo.

Chamar o método gc sugere que a Java Virtual Machine dedique esforços para reciclar objetos não utilizados, a fim de disponibilizar a memória que eles ocupam atualmente para reutilização rápida. Quando o controle retorna da chamada do método, a Java Virtual Machine se esforça ao máximo para recuperar o espaço de todos os objetos descartados.

Não recomendado.

Edit: eu escrevi a resposta original em 2009. Agora é 2015.

Os coletores de lixo ficaram cada vez melhores nos ~ 20 anos que o Java existe. Neste ponto, se você estiver chamando manualmente o coletor de lixo, convém considerar outras abordagens:

  • Se você estiver forçando o GC em um número limitado de máquinas, pode valer a pena afastar um balanceador de carga da máquina atual, aguardar o término da veiculação de clientes conectados, o tempo limite após um período de interrupção de conexões e apenas um pouco -reinicie a JVM. Essa é uma solução terrível, mas se você estiver olhando para System.gc (), reinicializações forçadas podem ser uma possível interrupção.
  • Considere usar um coletor de lixo diferente. Por exemplo, o (novo nos últimos seis anos) coletor G1 é um modelo de baixa pausa; ele usa mais CPU em geral, mas é melhor nunca forçar uma parada difícil na execução. Como as CPUs de servidor agora quase todas têm múltiplos núcleos, é uma troca muito boa de ter disponível.
  • Veja o uso da memória de ajuste das bandeiras. Especialmente nas versões mais recentes do Java, se você não tiver tantos objetos em execução a longo prazo, considere aumentar o tamanho do newgen na pilha. newgen (jovem) é onde novos objetos são alocados. Para um servidor da Web, tudo o que é criado para uma solicitação é colocado aqui e, se esse espaço for muito pequeno, o Java gastará um tempo extra atualizando os objetos para uma memória de vida mais longa, onde é mais caro matar. (Se o newgen for um pouco pequeno demais, você pagará por isso.) Por exemplo, no G1:
    • XX: G1NewSizePercent (o padrão é 5; provavelmente não importa.)
    • XX: G1MaxNewSizePercent (o padrão é 60; provavelmente aumente isso.)
  • Considere dizer ao coletor de lixo que você não está bem com uma pausa mais longa. Isso causará execuções de GC mais frequentes, para permitir que o sistema mantenha o restante de suas restrições. No G1:
    • XX: MaxGCPauseMillis (o padrão é 200.)
Dean J
fonte
1
Comentando em meu próprio post, isso geralmente não faz nada, e chamá-lo repetidamente pode fazer com que a JVM se torne instável e outros enfeites. Também pode atropelar seu cachorro; abordagem com cautela.
Dean J
1
Gostaria de colocar ênfase na "sugere" parte de "Chamar o método gc sugere que a JVM expandir esforço"
Matt b
2
@Jesper, a resposta de Dean afirma "sugere". Na verdade, ele postou a documentação exata de javadocs do método ...
Matt b
2
@ Monkey Software: Sim, eu poderia ter editado. Mas como Dean J estava obviamente ativo (publicando apenas alguns minutos atrás), achei que era uma cortesia pedir a ele para fazê-lo. Se ele não tivesse, eu teria voltado aqui e feito a edição e excluído o meu comentário.
11119 Daniel Pryden
1
Também vale a pena dizer POR QUE não é recomendado. Se a JVM prestar atenção à "sugestão" de executar o GC, quase certamente fará com que seu aplicativo seja mais lento, possivelmente por muitas ordens de magnitude!
22615 Stephen C #
11

* "Pessoalmente, confio nas variáveis ​​de nulidade como espaço reservado para futura exclusão adequada. Por exemplo, dedico um tempo para anular todos os elementos de uma matriz antes de excluir (tornar nula) a própria matriz."

Isso é desnecessário. A maneira como o Java GC funciona é encontrar objetos que não têm referência a eles; portanto, se eu tiver um Objeto x com uma referência (= variável) a que aponte para ele, o GC não o excluirá, porque há uma referência para esse objeto:

a -> x

Se você nulo a, isso acontece:

a -> null
     x

Portanto, agora x não tem uma referência apontando para ele e será excluído. O mesmo acontece quando você define a para fazer referência a um objeto diferente de x.

Portanto, se você tem um arr arr que faz referência aos objetos x, ye z e uma variável a que faz referência ao array, fica assim:

a -> arr -> x
         -> y
         -> z

Se você nulo a, isso acontece:

a -> null
     arr -> x
         -> y
         -> z

Portanto, o GC considera arr como não tendo nenhuma referência definida e a exclui, o que fornece essa estrutura:

a -> null
     x
     y
     z

Agora o GC encontra x, ye z e os exclui também. Anular cada referência na matriz não melhorará nada, apenas consumirá o tempo e o espaço da CPU no código (dito isso, não será prejudicial além disso. O GC ainda poderá executar da maneira que deve )

Dakkaron
fonte
5

Um motivo válido para querer liberar memória de qualquer programa (java ou não) é disponibilizar mais memória para outros programas no nível do sistema operacional. Se meu aplicativo java estiver usando 250 MB, convém forçá-lo a 1 MB e disponibilizar os 249 MB para outros aplicativos.

Yios
fonte
Se você precisar liberar explicitamente um pedaço de 249 MB, em um programa Java, o gerenciamento de memória não seria a primeira coisa em que eu gostaria de trabalhar.
Marc DiMillo
3
Porém, liberar armazenamento dentro do heap Java não (no caso geral) torna o armazenamento disponível para outros aplicativos.
Hot Licks
5

Para estender a resposta e o comentário de Yiannis Xanthopoulos e Hot Licks (desculpe, ainda não posso comentar!), Você pode definir opções de VM como este exemplo:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

No meu jdk 7, isso liberará memória não utilizada da VM se mais de 30% do heap ficar livre após o GC quando a VM estiver ociosa. Você provavelmente precisará ajustar esses parâmetros.

Embora eu não tenha visto isso enfatizado no link abaixo, observe que alguns coletores de lixo podem não obedecer a esses parâmetros e, por padrão, o java pode escolher um deles para você, caso você tenha mais de um núcleo (daí o argumento UseG1GC acima )

Argumentos da VM

Atualização: Para o java 1.8.0_73, vi a JVM ocasionalmente liberar pequenas quantidades com as configurações padrão. Parece fazê-lo apenas se ~ 70% da pilha não for usada .. não sei se seria mais agressivo se o sistema operacional estivesse com pouca memória física.

nsandersen
fonte
4

Eu fiz experiências sobre isso.

É verdade que System.gc();apenas sugere executar o Garbage Collector.

Mas chamar System.gc();depois de definir todas as referências para null, melhorará o desempenho e a ocupação da memória.

Hemant Yadav
fonte
Eu acho que você não pode dizer com certeza que "chamar System.gc (); depois de definir todas as referências para null, melhorará o desempenho e a ocupação da memória". Porque existe uma enorme complexidade computacional do System.gc (). E mesmo depois de chamar System.gc () e realmente coletar o lixo, a jvm pode não devolver a memória ao sistema operacional ou ao sistema. A JVM pode reter a memória para referência futura. Veja esta resposta .
Dr. Abu Nafee Ibna Zahid
3

Se você realmente deseja alocar e liberar um bloco de memória, pode fazer isso com ByteBuffers diretos. Existe até uma maneira não portátil de liberar a memória.

No entanto, como foi sugerido, apenas porque você precisa liberar memória em C, não significa que seja uma boa ideia fazer isso.

Se você acha que realmente tem um bom caso de uso gratuito (), inclua-o na pergunta para que possamos ver o que você está tentando fazer, é bem provável que exista uma maneira melhor.

Peter Lawrey
fonte
3

Inteiramente de javacoffeebreak.com/faq/faq0012.html

Um encadeamento de baixa prioridade cuida da coleta de lixo automaticamente para o usuário. Durante o tempo ocioso, o encadeamento pode ser chamado e pode começar a liberar memória alocada anteriormente para um objeto em Java. Mas não se preocupe - ele não excluirá seus objetos!

Quando não há referências a um objeto, torna-se um jogo justo para o coletor de lixo. Em vez de chamar alguma rotina (como livre em C ++), você simplesmente atribui todas as referências ao objeto para null, ou atribui uma nova classe à referência.

Exemplo:

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Se o seu código estiver prestes a solicitar uma grande quantidade de memória, convém solicitar que o coletor de lixo comece a recuperar espaço, em vez de permitir que faça isso como um encadeamento de baixa prioridade. Para fazer isso, adicione o seguinte ao seu código

System.gc();

O coletor de lixo tentará recuperar o espaço livre e seu aplicativo poderá continuar executando, com o máximo de memória possível (os problemas de fragmentação da memória podem se aplicar a determinadas plataformas).

Nome em Exibição
fonte
1

No meu caso, como meu código Java deve ser portado para outras linguagens em um futuro próximo (principalmente C ++), eu pelo menos quero prestar um bom serviço para liberar memória adequadamente, para ajudar no processo de portabilidade posteriormente.

Pessoalmente, confio nas variáveis ​​de nulidade como espaço reservado para futura exclusão adequada. Por exemplo, dedico um tempo para anular todos os elementos de uma matriz antes de realmente excluir (tornar nulo) a própria matriz.

Mas meu caso é muito particular e sei que estou recebendo resultados de desempenho ao fazer isso.

Oskuro
fonte
1

* "Por exemplo, digamos que você tenha declarado uma lista no início de um método que cresceu em tamanho para ser muito grande, mas só era necessária até a metade do método. Nesse momento, era possível definir a referência da lista como nula para permitir que o coletor de lixo potencialmente recupere esse objeto antes que o método seja concluído (e a referência fica fora do escopo de qualquer maneira). " *

Isso está correto, mas esta solução pode não ser generalizável. Ao definir uma referência de objeto de lista como nula - disponibilizará memória para coleta de lixo, isso é válido apenas para um objeto de lista de tipos primitivos. Se, em vez disso, o objeto List contiver tipos de referência, a configuração do objeto List = null não desreferirá - qualquer um dos tipos de referência contidos - na lista. Nesse caso, definir o objeto List = null tornará órfão os tipos de referência contidos cujos objetos não estarão disponíveis para coleta de lixo, a menos que o algoritmo de coleta de lixo seja inteligente o suficiente para determinar que os objetos foram órfãos.

Gothri
fonte
1
Na verdade, isso não é verdade. O coletor de lixo Java é inteligente o suficiente para lidar com isso corretamente. Se você anular a lista (e os objetos na lista não tiverem outras referências a eles), o GC poderá recuperar todos os objetos na lista. Ele pode optar por não fazer isso no momento, mas os recuperará eventualmente. O mesmo vale para referências cíclicas. Basicamente, a maneira como o GC funciona é procurar explicitamente objetos orfaneados e depois recuperá-los. Este é todo o trabalho de um GC. A maneira como você o descreve tornaria um GC totalmente inútil.
Dakkaron
1

Por meio do java, fornece coleta de lixo automática às vezes, você deseja saber o tamanho do objeto e a quantidade restante. Memória livre usando programaticamente import java.lang;e Runtime r=Runtime.getRuntime(); obter valores de memória usando mem1=r.freeMemory();a memória livre para chamar o r.gc();método e a chamadafreeMemory()

Benjamin
fonte
1

A recomendação do JAVA é atribuir a null

De https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

A atribuição explícita de um valor nulo a variáveis ​​que não são mais necessárias ajuda o coletor de lixo a identificar as partes da memória que podem ser recuperadas com segurança. Embora o Java forneça gerenciamento de memória, ele não impede vazamentos ou uso excessivo de memória.

Um aplicativo pode induzir vazamentos de memória, não liberando referências a objetos. Isso evita que o coletor de lixo Java recupere esses objetos e resulta no aumento da quantidade de memória usada. Anular explicitamente referências a variáveis ​​após seu uso permite que o coletor de lixo recupere memória.

Uma maneira de detectar vazamentos de memória é empregar ferramentas de criação de perfil e tirar instantâneos de memória após cada transação. Um aplicativo sem vazamento no estado estacionário mostrará uma memória ativa ativa constante após a coleta de lixo.

user3869837
fonte