2015-07-07 21 views
26

Khi tôi loại bỏ vòng lặp for, tôi nhận được một lỗi OutOfMemoryError. Khi tôi sử dụng cho vòng lặp tôi không nhận được lỗi. Bất cứ ai có thể giúp tôi hiểu được hành vi này?Thêm vào vòng lặp ngăn chặn OutOfMemoryError

public class JavaMemoryPuzzlePolite { 
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6); 

    public void f() { 
     { 
      System.out.println(dataSize); 
      byte[] data = new byte[dataSize]; 
     } 
     for (int i = 0; i < 1; i++) { 
      System.out.println("Please be so kind and release memory"); 
     } 
     System.out.println(dataSize); 
     byte[] data2 = new byte[dataSize]; 
    } 

    public static void main(String[] args) { 
     JavaMemoryPuzzlePolite jmp = new JavaMemoryPuzzlePolite(); 
     jmp.f(); 
    } 
} 
+0

Xem thêm http://stackoverflow.com/a/938080/545127 – Raedwald

Trả lời

28

Phương thức f() được thực hiện trong khung giải nghĩa. Các khung được giải thích hoạt động khác với các khung được biên dịch bởi JIT. Dưới đây là làm thế nào nó sẽ tìm trong mã giả mà không có sự cho vòng lặp:

1. Allocate dataSize bytes of memory 
2. Store it into variable slot #1 
3. Allocate dataSize bytes of memory 
4. Store it into variable slot #1 

Vì vậy, bạn có OutOfMemoryError trên bướC# 3 như byte[] mảng cũ vẫn nằm trong biến # 1. Tuy nhiên thêm các vòng lặp for (trên thực tế thêm một biến i) làm điều khác nhau:

1. Allocate dataSize bytes of memory 
2. Store it into variable slot #1 
3. Store 0 to slot #1 (thus byte[] array is now eligible for GC) 
4. Do the for loop 
5. Allocate dataSize bytes of memory 
6. Store it into variable slot #2 

đây khi bạn phân bổ các mảng mới ở bướC# 5, mảng đầu tiên có thể đã được thu gom rác thải.

Lưu ý rằng trình biên dịch JIT có thể hoạt động thông minh hơn và hủy liên kết mảng đầu tiên khỏi biến vì nó không được sử dụng (trong trường hợp cụ thể của bạn, nó sẽ không phân bổ nó).

Cũng lưu ý rằng trong trường hợp cụ thể của bạn, kết quả phụ thuộc vào trình biên dịch java. ECJ (trình biên dịch Eclipse) đủ thông minh để không lưu trữ mảng đầu tiên vào biến vì nó không được sử dụng. Do đó bạn sẽ không nhận được OutOfMemoryError trong lớp được biên dịch ECJ ngay cả khi không có vòng lặp for.

Để biết thêm chi tiết, bạn có thể xem xét đầu ra tháo gỡ bytecode do tiện ích javap cung cấp và xem cách sử dụng lại các vị trí biến.

+0

bạn có thể giải thích bước 3 thêm một chút không? '3. Lưu trữ 0 đến vị trí số 1 (do đó mảng byte [] hiện đủ điều kiện cho GC) ' – asgs

+3

@asgs, mỗi biến cục bộ có vị trí của nó được gán bởi trình biên dịch javac. 'Dữ liệu' được gán cho vị trí số 1. Sau đó, khối kết thúc, do đó, khe có thể được tái sử dụng và biến tiếp theo sẽ có vị trí số 1 là tốt. Với vòng lặp, biến tiếp theo là 'i'. Vì vậy, 'int i = 0' được dịch thành' iconst_0/istore_1' là "Lưu trữ 0 đến vị trí số 1" trong câu trả lời của tôi. Trình thông dịch không quan tâm nếu nó thực sự là một biến mới, nó chỉ thay thế giá trị trước đó, do đó nó trở nên không được chấp nhận. –

+0

@TagirValeev hoàn hảo, cảm ơn! Không phải 'iconst_0/istore_1' có nghĩa là" lưu trữ 1 đến vị trí số 0 "thay vì" Lưu trữ 0 vào vị trí số 1 ". – asgs

Các vấn đề liên quan