OOME kann abgefangen werden, ist jedoch im Allgemeinen nutzlos, je nachdem, ob die JVM in der Lage ist, einige Objekte zu sammeln, wenn der Fang erreicht ist, und wie viel Heapspeicher bis zu diesem Zeitpunkt noch vorhanden ist.
Beispiel: In meiner JVM wird dieses Programm vollständig ausgeführt:
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");
}
}
Wenn Sie jedoch nur eine einzelne Zeile in den Fang einfügen, sehen Sie, wovon ich spreche:
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");
}
}
Das erste Programm läuft einwandfrei, da die JVM bei Erreichen des Catch erkennt, dass die Liste nicht mehr verwendet wird (diese Erkennung kann auch eine Optimierung sein, die zur Kompilierungszeit vorgenommen wird). Wenn wir also die print-Anweisung erreichen, wurde der Heap-Speicher fast vollständig freigegeben, sodass wir jetzt einen großen Handlungsspielraum haben, um fortzufahren. Dies ist der beste Fall.
Wenn der Code jedoch so angeordnet ist, dass die Liste ll
nach dem Abfangen des OOME verwendet wird, kann die JVM ihn nicht erfassen. Dies geschieht im zweiten Snippet. Das OOME, ausgelöst durch eine neue Long-Erstellung, wird abgefangen, aber bald erstellen wir ein neues Objekt (eine Zeichenfolge in der System.out,println
Zeile), und der Heap ist fast voll, sodass ein neues OOME ausgelöst wird. Dies ist der schlimmste Fall: Wir haben versucht, ein neues Objekt zu erstellen, wir sind fehlgeschlagen, wir haben das OOME abgefangen, ja, aber jetzt löst die erste Anweisung, die neuen Heapspeicher benötigt (z. B. das Erstellen eines neuen Objekts), ein neues OOME aus. Denken Sie darüber nach, was können wir an diesem Punkt noch tun, wenn so wenig Speicher übrig bleibt? Wahrscheinlich nur aufregend. Daher das Nutzlose.
Einer der Gründe, warum die JVM keine Ressourcen sammelt, ist wirklich beängstigend: Eine gemeinsam genutzte Ressource, die auch von anderen Threads verwendet wird. Jeder mit einem Gehirn kann sehen, wie gefährlich das Fangen von OOME sein kann, wenn es in eine nicht experimentelle App jeglicher Art eingefügt wird.
Ich verwende eine Windows x86 32-Bit-JVM (JRE6). Der Standardspeicher für jede Java-App beträgt 64 MB.