OOME se puede capturar, pero generalmente será inútil, dependiendo de si la JVM puede recolectar basura en algunos objetos cuando se alcanza la captura, y cuánta memoria de pila queda en ese momento.
Ejemplo: en mi JVM, este programa se ejecuta hasta su finalización:
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");
}
}
Sin embargo, simplemente agregando una sola línea en la captura le mostrará de qué estoy hablando:
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");
}
}
El primer programa funciona bien porque cuando se alcanza la captura, la JVM detecta que la lista no se va a utilizar más (esta detección también puede ser una optimización realizada en tiempo de compilación). Entonces, cuando llegamos a la declaración de impresión, la memoria del montón se ha liberado casi por completo, por lo que ahora tenemos un amplio margen de maniobra para continuar. Este es el mejor caso.
Sin embargo, si el código está organizado de forma tal que la lista ll
se utiliza después de que se haya capturado el OOME, la JVM no podrá recopilarlo. Esto sucede en el segundo fragmento. El OOME, desencadenado por una nueva creación Long, se detecta, pero pronto estamos creando un nuevo Objeto (una Cadena en la System.out,println
línea), y el montón está casi lleno, por lo que se lanza un nuevo OOME. Este es el peor de los casos: intentamos crear un nuevo objeto, fallamos, capturamos el OOME, sí, pero ahora la primera instrucción que requiere una nueva memoria de pila (por ejemplo: crear un nuevo objeto) arrojará un nuevo OOME. Piénsalo, ¿qué más podemos hacer a estas alturas con tan poca memoria ?. Probablemente solo saliendo. De ahí lo inútil.
Entre las razones por las que la JVM no recopila recursos, una es realmente aterradora: un recurso compartido con otros subprocesos que también lo utilizan. Cualquiera con cerebro puede ver lo peligroso que puede ser atrapar OOME si se inserta en alguna aplicación no experimental de cualquier tipo.
Estoy usando una JVM de Windows x86 de 32 bits (JRE6). La memoria predeterminada para cada aplicación Java es de 64 MB.