Informações

Tipo: Artigo
Data de Publicação: 01/01/2004
Revisado em: 01/01/2004

Vote!

  • Currently 4,6/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags Relacionadas

jvm otimizacao memoria gc

Comentários ( 3 )

Imprimir

Otimizando o Uso da Memória em aplicações Java

por:

Gleydson Lima (gleydson@jeebrasil.com.br)
Galileu Batista (galileu@j2eebrasil.com.br)

Em Java, diferente de outras linguagens como C e C++, o programador não precisa desalocar memória, usando funções como free e delete. Java possui um coletor de lixo, uma thread de baixa prioridade que executa junto com a Java Virtual Machine. Entenda como ele funciona lendo este artigo.

Em Java, diferente de outras linguagens como C e C++, o programador não precisa desalocar memória, usando funções como free e delete. Java possui um coletor de lixo, uma thread de baixa prioridade que executa junto com a Java Virtual Machine. Sua função é gerenciar a utilização de memória, procuranado áreas de memória que não estejam mais em uso para realizar a liberação das mesmas.

A execução do coletor de lixo é transparente para o programador, porém, podemos em algumas situações desejar usar artifícios para otimizar o uso da memória pela JVM.

Funcionamento do coletor de lixo

O coletor de lixo libera a memória mantida por um objeto se não houver mais nenhuma referência para ele. Dependendo do algoritmo da coleta de lixo usado pela máquina virtual Java no qual o código está sendo executado, nem todo objeto não referenciado será desalocado pelo coletor de lixo. Um objeto mais antigo, de longa duração, é menos provável de estar não referenciado do que um objeto novo, de modo que um algoritmo comum para coleta de lixo é analisar os objetos mais antigos com menos freqüência do que os objetos mais novos.

Podemos ter um métrica da eficiência da implementação do coletor de lixo utilizado pela sua JVM com o código abaixo:

Runtime rt = Runtime.getRuntime();
long mem = rt.freeMemory();
System.out.printn("Memória Livre: " + mem);

// ... algum código

System.gc();

// ...  

mem = rt.freeMemory(); 
System.out.println("Memória Livre: " + mem);

O código acima obtém o ambiente de execução com o método estático getRuntime() da classe Runtime. O método freeMemory() retorna a memória disponível no ambiente runtime. A linha System.gc() faz a chamada explícita do coletor de lixo. Em algumas implementações da JVM, o coletor de lixo pode ser ativado através da chamada ao método System.gc(), embora a especificação não garanta isso. De todo modo, a maior parte das JVMs fazem a chamada do coletor implicitamente quando a memória está baixa ou quando a CPU está inativa por um certo período de tempo.

Problemas com a utilização de memória podem surgir quando os objetos contém variáveis de instância que são iniciadas no construtor, ocupam um grande quantidade de memória e não são mais utililzadas no restante da aplicação. Isso pode degradar o desempenho de um código.

Liberando um objeto

Uma maneira de tornar um objeto candidato a ser retirado da memória pelo coletor de lixo é atribuindo o valor null aa sua referência. Com isso, o objeto torna-se não referenciado e pode ser retirado da memória pelo coletor de lixo. O programa abaixo ilustra essa técnica:

import java.util.*; 

class GarbageExample { 

	private static Vector vetor; 

	public static void main(String args) { 

		vetor = new Vector();
		for (int a=0; a < 500; a++) 
     			vetor.addElement(new StringBuffer("teste"));

		Runtime rt = Runtime.getRuntime();

		System.out.println("Memória Livre: " + rt.freeMemory()); 

		vetor = null;

		System.gc();

		System.out.println("Memória Livre: " + rt.freeMemory()); 

	} 
}

O código acima aloca 500 objetos StringBuffer e os coloca dentro de um Vector. O objeto da classe Vector é referenciado pela variável vetor. Quando atribui-se o valor null para a variável vetor o conteúdo do objeto Vector (500 objetos StringBuffer) não possui mais referência na aplicação. A chamada explícita ao coletor de lixo faz a limpeza desse objeto. O programa exibe a quantidade de memória livre antes e depois da execução do coletor de lixo.

Essa técnica pode solucionar o problema de manter grandes quantidades de memória desnecessárias. Para usar o mínimo de memória possível, os objetos que precisem existir durante um tempo maior devem ser os menores possíveis. Do outro lado, os grandes objetos deverão existir pelo menor tempo possível.

Essa técnica podem ajudar, em algun casos, somente o desempenho das aplicações Java que executam na mesma JVM e não necessariamente de outros procesos rodando no sistema. Isso porque muitos algoritmos de gerenciamento de heap alocam antecipadamente uma parte da memória para ser usada pelo seu código. Ou seja, você libera a memória da máquina virtual Java, o que não afetará outras aplicações não-Java, pelo fato da JVM pre-alocar o heap que ela precisa.

A qualquer momento pode-se solicitar a execução do coletor de lixo (sem garantias de execução), chamando o método System.gc(). Porém, é necessário analisar o impacto de desempenho na sua aplicação. Muitos algoritmos de coleta de lixo suspendem todas as outras threads antes de entrar em execução. Isso garante que, quando o coletor de lixo for executado, ele terá acesso completo à memória no heap e poderá realizar suas tarefas com segurança, sem que seja interrompido por outra thread. Quando o coletor de lixo termina seu trabalho, todas as threads que ele suspendeu são retomados.

O coletor de lixo de Java é executado com um grande freqüência, sua chamada explícita dificilmente é necessária. Porém, em alguns casos pode tornar-se conveniente. É recomendado evitar a chamada ao método System.gc() pelo bloqueio que provoca. É recomendado também nunca fazer essa chamada explícita dentro de loops.

O método finalize()

O programador pode definir o método finalize() que será chamado sempre antes do objeto ser retirado da memória pelo coletor de lixo. A aplicação abaixo ilustra isso:

import java.util.*; 

class GarbageExample { 

	private static MeuVetor vetor; 

	public static void main(String args) {

		vetor = new MeuVetor();

		for (int a=0; a <500; a++)
			vetor.addElement(new StringBuffer("teste"));
	        Runtime rt = Runtime.getRuntime();

		System.out.println("Memória Livre: " + rt.freeMemory());
		
		vetor = null; // deixa os 500 StringBuffers sem referência
		
		System.gc(); 
		
		System.out.println("Memória Livre: " + rt.freeMemory());
	} 
} 



	
class MeuVetor extends Vector { 
	public void finalize() { 
		 System.out.println("Vou ser coletado!!");
	} 
} 

O método finalize da classe MeuVetor é executado antes do objeto ser coletado da memória, a saída deste programa é:

Memória Livre: 459808
Vou ser coletado!!
Memória Livre: 600664

Neste artigo procurou-se dar uma idéia de como funciona a coleta de lixo em Java além de apresentar/discutir algumas dicas para diminuir os requerimentos de memória em aplicações Java.

Comentários (3)

Ótimo, extamente o que procurava. Com o nível de detallhes ravoavelmente alto, necessário para o bom entendimento. Parabéns e obrigado.
postado por Uchoa em 08/01/2007 às 23:21
olá gostaria se alguém poderia me dizer algo sobre coletores, mas coletores que mostram a taxa de usabilidade de memória em um determinado momento ? desde já agradeço !
postado por Ricardo em 12/03/2008 às 23:21
Gostaria de saber quais são os procedimentos para se percorrer um vetor e armazenar os dados em um vetor?
postado por João Paulo Lemos Prestes em 28/03/2008 às 23:21
Comente!

Observações

Os campos em negrito são obrigatórios.

Para evitar problemas, este espaço é moderado. Após o envio do comentário será necessário aguardar pela sua aprovação.