OOME pode ser capturado, mas geralmente será inútil, dependendo se a JVM é capaz de coletar como lixo alguns objetos quando a captura é alcançada e de quanta memória de heap resta nesse momento.
Exemplo: na minha JVM, este programa é executado até a conclusão:
import java.util.LinkedList;
import java.util.List;
public class OOMErrorTest {
public static void main(String[] args) {
List<Long> ll = new LinkedList<Long>();
try {
long l = 0;
while(true){
ll.add(new Long(l++));
}
} catch(OutOfMemoryError oome){
System.out.println("Error catched!!");
}
System.out.println("Test finished");
}
}
No entanto, apenas adicionar uma única linha na captura mostrará do que estou falando:
import java.util.LinkedList;
import java.util.List;
public class OOMErrorTest {
public static void main(String[] args) {
List<Long> ll = new LinkedList<Long>();
try {
long l = 0;
while(true){
ll.add(new Long(l++));
}
} catch(OutOfMemoryError oome){
System.out.println("Error catched!!");
System.out.println("size:" +ll.size());
}
System.out.println("Test finished");
}
}
O primeiro programa funciona bem porque quando o catch é atingido, a JVM detecta que a lista não será mais usada (esta detecção também pode ser uma otimização feita em tempo de compilação). Portanto, quando alcançamos a instrução print, a memória heap foi liberada quase totalmente, portanto, agora temos uma ampla margem de manobra para continuar. Este é o melhor caso.
No entanto, se o código for organizado como a lista ll
é usada após o OOME ter sido capturado, a JVM não será capaz de coletá-lo. Isso acontece no segundo trecho. O OOME, acionado por uma nova criação Longa, é capturado, mas logo estamos criando um novo Objeto (uma String na System.out,println
linha), e o heap está quase cheio, então um novo OOME é lançado. Este é o pior cenário: tentamos criar um novo objeto, falhamos, capturamos o OOME, sim, mas agora a primeira instrução que requer uma nova memória heap (por exemplo: criar um novo objeto) irá lançar um novo OOME. Pense nisso, o que mais podemos fazer neste momento com tão pouca memória sobrando ?. Provavelmente está saindo. Daí o inútil.
Entre os motivos da JVM não coletar recursos, um é realmente assustador: um recurso compartilhado com outras threads também fazendo uso dele. Qualquer pessoa com cérebro pode ver como é perigoso capturar OOME se inserido em algum aplicativo não experimental de qualquer tipo.
Estou usando uma JVM (JRE6) do Windows x86 de 32 bits. A memória padrão para cada aplicativo Java é 64 MB.