Monthly Archives: November 2021

Effective Java – Item 7 – Elimine referências obsoletas a objetos

Gerenciamento de memória em linguagens com Java, que possuem mecanismo de coleta de lixo, ou garbage collection mechanism, podem evitar problemas de perda de memória, ou “memory leaks“.

As “Memory Leaks” ocorrem quando objetos que já não são utilizados ainda ocupam espaço na memória. Esses objetos estão no Heap e suas variáveis na Stack, no caso do Java.

Não é por haver um mecanismo de coleta de lixo que o programador não deva se preocupar com problemas de memória que um código, até mesmo um simples, pode causar.

Perdas de memória podem gerar uma OutOfMemoryError mas o maior problema é que, na maioria da vezes, ela ocorre de forma silienciosa. E apenas podem ser descobertas com uma inspeção minuciosa do código e com o auxílio de ferramentas de debugging, conhecidas como “heap profiler“.

A classe abaixo armazena um array de objetos, permite o push e o pop de elementos e a cada push, dobra de tamanho.

// Can you spot the "memory leak"?
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}

O problema dela é no método pop. Quando um objeto é retornado e o tamanho diminuído, a stack ainda mantém uma referência obsoleta a esse objeto. Nessa caso, toda referência que estiver fora da “porção ativa” do array serão obsoletos. A porção ativa equivale aos elementos que o índice for menor que o tamanho do array.

Perda de memórias em linguagens como o Java são também conhecidas como retenção não intencionais de objetos, ou “unintentional object retentions“. Esses objetos ficam excluídos do coleta de lixo assim como objetos que ele possa referênciar, e assim por diante.

E a solução? Tornar nulas as referências assim que um objeto se tornar obsoleto. Segue o treco do código com a solução.

public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}

Mas cuidado. Tornar nula referências a objetos deve ser exceção e não regra. E a melhor maneira de eliminar referências obsoletas é definar cada variável num menor escopo possível.

Então quando se deve usar o “null out”? Quais aspectos tornam uma classe suscetível a “memory leaks”? De maneira simples, sempre que houver uma espécie de memória e que seja gerida por ela. No caso da classe Stack, é o array de elementos e seus métodos para incluir e remover. O garbage collector não tem como saber se um elemento está inválido, uma vez que guarda a referência aos objetos e não eles em si. Portanto, isso deve ser feito manualmente pelo programador.

Até o próximo item.