2013-07-25 26 views
5

Đây là mã của tôiTại sao tôi không nhận được PhantomReference từ ReferenceQueue cho một đối tượng hoàn chỉnh?

public class FinalizableObject { 

    @Override 
    protected void finalize() throws Throwable { 
     System.out.println("finalize() invoked for " + this); 
     super.finalize(); 
    } 
} 

public class Main { 
    private static void test() throws InterruptedException { 
     ReferenceQueue<FinalizableObject> rq = new ReferenceQueue<FinalizableObject>(); 
     FinalizableObject obj = new FinalizableObject(); 
     PhantomReference<FinalizableObject> pr1 = new PhantomReference<FinalizableObject>(obj, rq); 
     obj = null; 
     System.gc(); 
     Reference<? extends Object> ref = rq.remove(); 
     System.out.print("remove " + ref + " from reference queue\n"); 
    } 
    public static void main(String[] args) { 
     try { 
      test(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Nó rất lạ, rq.remove() sẽ bị chặn vĩnh viễn. Tại sao tham chiếu ma trận của đối tượng cuối cùng của tôi không thể được đưa vào hàng đợi tham chiếu? Nó đã được GC thu thập?

Trả lời

1

Vấn đề là với phương pháp không tầm thường của bạn finalize(). Trong thực hiện mặc định (trong lớp Object) phương thức này thực sự trống. Khi triển khai của nó không trống, thì không đảm bảo rằng đối tượng sẽ được thu thập ngay lập tức sau khi gọi finalize().

Nếu bạn sẽ sửa đổi chương trình của bạn trong phong cách như vậy:

for (int i = 0; i < 1000; i++) 
     System.gc(); 

ví dụ: bạn sẽ gọi GC sau đó thêm một lần - nó có thể dẫn đến các đối tượng rq được thu thập đầy đủ (thử nghiệm trên máy tính của tôi).

Ngoài ra, tôi đề nghị bạn theo các liên kết để khám phá:

  1. Java: PhantomReference, ReferenceQueue and finalize
  2. Discovering Objects with Non-trivial Finalizers
  3. The Secret Life Of The Finalizer: page 2 of 2

UPD: Một cách khác: bạn phải giữ trong tâm trí, mà các phương thức không phổ biến là finalize() được gọi bởi đặc biệt Thread-Thread, mà còn ave được thu thập. Vì vậy, đối đầy đủ pr thu thập bạn có thể làm những việc như vậy:

a) tạo ra lá cờ bên Main phương pháp:

public static volatile boolean flag; 

b) thiết lập cờ trong finalize() phương pháp:

@Override 
protected void finalize() throws Throwable { 
    System.out.println("finalize() invoked for " + this); 
    super.finalize(); 
    Main.flag = true; 
} 

c) kiểm tra cờ cho đúng và sau đó gọi gc() lần nữa:

System.gc(); 
    while (!flag) Thread.sleep(10); 
    System.gc(); 
+0

Cảm ơn bạn rất nhiều.
Tôi biết FinalizerReferenceQueue và FinalizerDeamon, 2 lần GC có thể thu thập hoàn toàn một đối tượng cuối cùng. Nhưng tôi không thể hiểu tại sao 100 lần GC sẽ kích hoạt nó được thu thập (thậm chí 20 lần GC không thể kích hoạt enqueue).
Mặt khác, nếu tôi thay thế PhantomReference bằng WeakReference, nó có thể loại bỏ tham chiếu rất nhanh.

+0

@DevboardFan Điều này là bởi vì để gọi phương thức 'finalize()' không tầm thường, JVM xây dựng đặc biệt Finalizer-Thread, cũng phải được thu thập và hoàn thành. – Andremoniy

+0

Cảm ơn bạn rất nhiều! –

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