OOME peut être attrapé mais sera généralement inutile, selon que la JVM est capable de ramasser certains objets lorsque la capture est atteinte, et combien de mémoire de tas reste à ce moment-là.
Exemple: dans ma JVM, ce programme s'exécute jusqu'à la fin:
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");
}
}
Cependant, le simple fait d'ajouter une seule ligne sur la capture vous montrera de quoi je parle:
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");
}
}
Le premier programme fonctionne correctement car lorsque la capture est atteinte, la JVM détecte que la liste ne sera plus utilisée (cette détection peut également être une optimisation effectuée au moment de la compilation). Ainsi, lorsque nous atteignons l'instruction d'impression, la mémoire du tas a été libérée presque entièrement, nous avons donc maintenant une large marge de manœuvre pour continuer. C'est le meilleur des cas.
Cependant, si le code est organisé comme la liste ll
est utilisée après que l'OOME a été capturé, la JVM ne peut pas le collecter. Cela se produit dans le deuxième extrait. Le OOME, déclenché par une nouvelle création Long, est attrapé, mais bientôt nous créons un nouvel Object (une String dans la System.out,println
ligne), et le tas est presque plein, donc un nouveau OOME est lancé. C'est le pire des cas: nous avons essayé de créer un nouvel objet, nous avons échoué, nous avons attrapé le OOME, oui, mais maintenant la première instruction nécessitant une nouvelle mémoire de tas (par exemple: créer un nouvel objet) lancera un nouveau OOME. Pensez-y, que pouvons-nous faire d'autre à ce stade avec si peu de mémoire ?. Probablement juste en train de sortir. D'où l'inutile.
Parmi les raisons pour lesquelles la JVM ne collecte pas de ressources, l'une est vraiment effrayante: une ressource partagée avec d'autres threads l'utilisant également. N'importe qui avec un cerveau peut voir à quel point attraper OOME peut être dangereux s'il est inséré sur une application non expérimentale de tout type.
J'utilise une JVM Windows x86 32bits (JRE6). La mémoire par défaut pour chaque application Java est de 64 Mo.