OOME può essere catturato ma sarà generalmente inutile, a seconda se la JVM è in grado di raccogliere alcuni oggetti quando viene raggiunto il punto di cattura e quanta memoria heap è rimasta in quel momento.
Esempio: nella mia JVM, questo programma viene eseguito fino al completamento:
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");
}
}
Tuttavia, solo aggiungendo una singola linea sul fermo ti mostrerà di cosa sto parlando:
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");
}
}
Il primo programma funziona bene perché quando viene raggiunto il catch, la JVM rileva che l'elenco non verrà più utilizzato (questo rilevamento può essere anche un'ottimizzazione effettuata in fase di compilazione). Quindi, quando raggiungiamo l'istruzione print, la memoria heap è stata liberata quasi completamente, quindi ora abbiamo un ampio margine di manovra per continuare. Questo è il caso migliore.
Tuttavia, se il codice è organizzato in modo che l'elenco ll
venga utilizzato dopo che OOME è stato rilevato, la JVM non è in grado di raccoglierlo. Questo accade nel secondo snippet. L'OOME, attivato da una nuova creazione Long, viene catturato, ma presto stiamo creando un nuovo Object (una stringa nella System.out,println
linea) e l'heap è quasi pieno, quindi viene lanciato un nuovo OOME. Questo è lo scenario peggiore: abbiamo provato a creare un nuovo oggetto, abbiamo fallito, abbiamo catturato OOME, sì, ma ora la prima istruzione che richiede una nuova memoria heap (ad esempio: creazione di un nuovo oggetto) lancerà un nuovo OOME. Pensaci, cos'altro possiamo fare a questo punto con così poca memoria rimasta ?. Probabilmente sto solo uscendo. Da qui l'inutile.
Tra le ragioni per cui la JVM non raccoglie risorse, una fa davvero paura: una risorsa condivisa con altri thread che ne fanno uso. Chiunque abbia un cervello può vedere quanto può essere pericoloso catturare OOME se inserito su qualche app non sperimentale di qualsiasi tipo.
Sto usando una JVM di Windows x86 a 32 bit (JRE6). La memoria predefinita per ogni app Java è 64 MB.