OOME สามารถจับได้ แต่โดยทั่วไปจะไม่มีประโยชน์ขึ้นอยู่กับว่า JVM สามารถเก็บขยะบางวัตถุได้หรือไม่เมื่อถึงที่จับและจำนวนหน่วยความจำฮีปที่เหลือในเวลานั้น
ตัวอย่าง: ใน JVM ของฉันโปรแกรมนี้ทำงานจนเสร็จสมบูรณ์:
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");
}
}
อย่างไรก็ตามการเพิ่มบรรทัดเดียวในการจับจะแสดงให้คุณเห็นว่าฉันกำลังพูดถึงอะไร:
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");
}
}
โปรแกรมแรกทำงานได้ดีเนื่องจากเมื่อถึงจุดจับ JVM จะตรวจพบว่ารายการจะไม่ถูกใช้อีกต่อไป (การตรวจจับนี้อาจเป็นการเพิ่มประสิทธิภาพในเวลาคอมไพล์) ดังนั้นเมื่อเราไปถึงคำสั่งพิมพ์หน่วยความจำฮีปได้รับการปลดปล่อยเกือบทั้งหมดดังนั้นตอนนี้เราจึงมีการซ้อมรบที่กว้างเพื่อดำเนินการต่อ นี่เป็นกรณีที่ดีที่สุด
อย่างไรก็ตามหากมีการจัดเรียงรหัสเช่นรายการll
ถูกใช้หลังจาก OOME ถูกจับแล้ว JVM จะไม่สามารถรวบรวมได้ สิ่งนี้เกิดขึ้นในตัวอย่างข้อมูลที่สอง OOME ที่ถูกเรียกโดยการสร้างแบบยาวใหม่จะถูกจับ แต่ในไม่ช้าเรากำลังสร้างวัตถุใหม่ (สตริงในSystem.out,println
บรรทัด) และฮีปใกล้จะเต็มแล้วดังนั้น OOME ใหม่จึงถูกโยนทิ้งไป นี่เป็นสถานการณ์ที่เลวร้ายที่สุด: เราพยายามสร้างวัตถุใหม่เราล้มเหลวเราจับ OOME ได้ แต่ตอนนี้คำสั่งแรกที่ต้องใช้หน่วยความจำฮีปใหม่ (เช่นการสร้างวัตถุใหม่) จะทำให้ OOME ใหม่เกิดขึ้น ลองคิดดูว่าเราจะทำอะไรได้อีกบ้างในตอนนี้ที่เหลือความทรงจำเพียงเล็กน้อย?. อาจเป็นเพียงการออก ดังนั้นไร้ประโยชน์
ในบรรดาเหตุผลที่ JVM ไม่รวบรวมทรัพยากรสิ่งหนึ่งที่น่ากลัวมากคือทรัพยากรที่ใช้ร่วมกันกับเธรดอื่น ๆ ก็ใช้ประโยชน์จากมันเช่นกัน ใครก็ตามที่มีสมองสามารถมองเห็นได้ว่าการจับ OOME นั้นอันตรายเพียงใดหากแทรกในแอปที่ไม่ได้ทดลองใด ๆ
ฉันใช้ Windows x86 32bits JVM (JRE6) หน่วยความจำเริ่มต้นสำหรับแต่ละแอป Java คือ 64MB