OOME có thể được bắt nhưng nhìn chung sẽ vô dụng, tùy thuộc vào việc JVM có thể thu thập một số đối tượng khi đạt đến bắt hay không và còn lại bao nhiêu bộ nhớ heap vào thời điểm đó.
Ví dụ: trong JVM của tôi, chương trình này chạy đến khi hoàn thành:
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");
}
}
Tuy nhiên, chỉ cần thêm một dòng duy nhất trên bản ghi sẽ cho bạn thấy điều tôi đang nói đế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("size:" +ll.size());
}
System.out.println("Test finished");
}
}
Chương trình đầu tiên chạy tốt vì khi đạt đến bắt, JVM phát hiện rằng danh sách sẽ không được sử dụng nữa (Phát hiện này cũng có thể là một tối ưu hóa được thực hiện tại thời điểm biên dịch). Vì vậy, khi chúng ta đến câu lệnh in, bộ nhớ heap đã được giải phóng gần như hoàn toàn, vì vậy bây giờ chúng ta có rất nhiều thao tác để tiếp tục. Đây là trường hợp tốt nhất.
Tuy nhiên, nếu mã được sắp xếp như danh sách ll
được sử dụng sau khi OOME đã được chốt, thì JVM không thể thu thập được. Điều này xảy ra trong đoạn mã thứ hai. OOME, được kích hoạt bởi một tạo Long mới, được bắt, nhưng chúng ta sẽ sớm tạo một Đối tượng mới (một Chuỗi trong System.out,println
dòng) và đống gần như đầy, vì vậy một OOME mới sẽ được ném ra. Đây là trường hợp xấu nhất: chúng tôi đã cố gắng tạo một đối tượng mới, chúng tôi không thành công, chúng tôi bắt được OOME, vâng, nhưng bây giờ lệnh đầu tiên yêu cầu bộ nhớ heap mới (ví dụ: tạo một đối tượng mới) sẽ tạo ra một OOME mới. Hãy nghĩ về nó, chúng ta có thể làm gì khác vào lúc này khi bộ nhớ còn lại rất ít ?. Có lẽ chỉ là xuất ngoại. Do đó vô ích.
Trong số các lý do khiến JVM không thu thập tài nguyên, có một lý do thực sự đáng sợ: một tài nguyên được chia sẻ với các luồng khác cũng đang sử dụng nó. Bất kỳ ai có não đều có thể thấy việc bắt OOME nguy hiểm như thế nào nếu được chèn vào một số ứng dụng phi thử nghiệm dưới bất kỳ hình thức nào.
Tôi đang sử dụng Windows x86 32bits JVM (JRE6). Bộ nhớ mặc định cho mỗi ứng dụng Java là 64MB.