Xin chào tất cả,Tại sao các biến của tôi không nằm ngoài phạm vi?
Tôi được dạy rằng khi một hàm trả về, các biến (trong phạm vi hàm đó) tự động thoát khỏi phạm vi để chúng ta không phải đặt chúng thành rỗng.
Tuy nhiên, điều này có vẻ không đúng.
Tôi có mã thử nghiệm tạo ra một java.lang.ref.PhantomReference trỏ đến một phiên bản java.lang.Object. Tham chiếu mạnh mẽ duy nhất cho đối tượng đó nằm trong phạm vi chức năng F.
Nói cách khác, khi hàm đó trả về, sẽ không còn tham chiếu mạnh đối tượng đó nữa và đối tượng sẽ được GC.
Tuy nhiên, dù tôi có cố gắng làm sao để bỏ đói bộ nhớ JVM, GC chỉ đơn giản từ chối thu thập đối tượng. Điều đáng ngạc nhiên là nếu tôi đặt biến thành null (obj = null;
), GC sẽ thu thập đối tượng.
Giải thích đằng sau sự kỳ quặc này là gì?
public class Test {
public static void main(String args[]) {
// currently testing on a 64-bit HotSpot Server VM, but the other JVMs should probably have the same behavior for this use case
Test test = new Test();
test.F(new Object());
}
public <T> void F(T obj) {
java.lang.ref.ReferenceQueue<T> ref_queue = new java.lang.ref.ReferenceQueue<T>();
java.lang.ref.PhantomReference<T> ref = new java.lang.ref.PhantomReference<T>(obj, ref_queue); // if this line isn't an assignment, the GC wouldn't collect the object no matter how hard I force it to
obj = null; // if this line is removed, the GC wouldn't collect the object no matter how hard I force it to
StartPollingRef(ref_queue);
GoOom();
}
private <T> void StartPollingRef(final java.lang.ref.ReferenceQueue<T> ref_queue) {
new java.lang.Thread(new java.lang.Runnable() {
@Override
public void run() {
System.out.println("Removing..");
boolean removed = false;
while (!removed) {
try {
ref_queue.remove();
removed = true;
System.out.println("Removed.");
} catch (InterruptedException e) { // ignore
}
}
}
}).start();
}
private void GoOom() {
try {
int len = (int) java.lang.Math.min(java.lang.Integer.MAX_VALUE, Runtime.getRuntime().maxMemory());
Object[] arr = new Object[len];
} catch (Throwable e) {
// System.out.println(e);
}
}
}
Điều này làm cho destructors hoàn thành botch-up như xa như lỗi thiết kế ngôn ngữ đi. – tchrist
@ Borealid Tuy nhiên không phải là toàn bộ điểm của java.lang.ref.PhantomReference sao cho chúng ta có thể được thông báo bất cứ khi nào một đối tượng rời khỏi heap? Sẽ không phải là một * kiêu ngạo * (vì thiếu một từ tốt hơn) JVM làm cho toàn bộ lớp vô dụng? – Pacerier
+1, và chỉ để làm rõ, một tham chiếu yếu nói chung là enqueued khi GC xác định rằng nó * có thể * được GCed, trong khi tham chiếu ma là enqueued khi GC có bước tiếp theo và thực sự GCs nó. Đó là một chút mơ hồ và không hoàn toàn chính xác; xem [ở đây] (http://docs.oracle.com/javase/6/docs/api/java/lang/ref/package-summary.html#reachability) để có định nghĩa chính xác hơn. – yshavit