簡體   English   中英

如何在即將收集的Java對象本身中獲取信息

[英]How to get java object itself when its about to be collected

如果我們無法控制對象的源代碼(無法強制實現某些接口或finally塊),那么如何在使用對象之前執行一段代碼(需要狀態)?

Java引用類型允許我們在其他人使對象非常可訪問時訪問該對象+如果我們使用引用隊列,則也可以在收集對象時收到通知,除非我的理解是錯誤的,這是引用類型所能做的一切您在任何時候使用該對象都是高度可訪問或已消失的對象,則為null。

我真正需要的是一種在要收集特定對象時獲得通知的方法。

引用API不允許檢索收集到的對象是有原因的:允許使收集到的對象再次可訪問(就像使用finalize()方法發生的那樣finalize() ,這完全不是我們想要的。

標准方法是創建引用類型的子類,以在特定的引用對象內存儲與引用對象關聯的信息,例如執行清理操作所需的一切。 當然,此信息不得包含對引用對象本身的強引用。

private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
static class IntegerPhantomReference extends PhantomReference<Integer> {
    final int value;
    public IntegerPhantomReference(Integer ref) {
        super(ref, QUEUE);
        value = ref.intValue();
    }
    public String toString() {
        return "Integer[value="+value+"]";
    }
}
private static final Set<IntegerPhantomReference> REGISTERED = new HashSet<>();
public static void main(String[] args) throws InterruptedException {
    List<Integer> stronglyReferenced = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        Integer object = new Integer(i);
        stronglyReferenced.add(object);
        REGISTERED.add(new IntegerPhantomReference(object));
    }
    gcAndPoll("initial");
    stronglyReferenced.removeIf(i -> i%2 == 0);
    gcAndPoll("after removing even");
    stronglyReferenced.clear();
    gcAndPoll("after remove all");
    if(REGISTERED.isEmpty()) System.out.println("all objects collected");
}
private static void gcAndPoll(String msg) throws InterruptedException {
    System.out.println(msg);
    System.gc(); Thread.sleep(100);
    for(;;) {
        Reference<?> r = QUEUE.poll();
        if(r == null) break;
        System.out.println("collected "+r);
        REGISTERED.remove(r);
    }
}
initial
after removing even
collected Integer[value=4]
collected Integer[value=8]
collected Integer[value=6]
collected Integer[value=2]
collected Integer[value=0]
after remove all
collected Integer[value=1]
collected Integer[value=5]
collected Integer[value=3]
collected Integer[value=7]
collected Integer[value=9]
all objects collected

為了完整起見,有一種黑客可以使已收集的對象復活,這將在Java 9中停止工作。

PhantomReference的文檔說:

與軟引用和弱引用不同,幻象引用在排隊時不會被垃圾收集器自動清除。

目前尚不清楚為什么要指定此參數, PhantomReferenceget()方法已被重寫以始終返回null ,正是為了避免從未清除此引用的事實中受益,這是不明確的。 由於這種特殊行為的目的尚不清楚,因此已從Java 9的規范中刪除了該行為,並且像其他任何引用一樣自動清除了這些引用。

但是對於以前的版本,可以使用帶有訪問覆蓋的反射來訪問引用對象,以完全執行API不允許的操作。 不用說,這只是出於提供信息的目的,並且強烈建議不要這樣做(並且如上所述,它停止在Java 9中工作)。

private static final ReferenceQueue<Integer> QUEUE = new ReferenceQueue<>();
private static final Set<PhantomReference<Integer>> REGISTERED = new HashSet<>();
public static void main(String[] args)
        throws InterruptedException, IllegalAccessException {
    List<Integer> stronglyReferenced = new ArrayList<>();
    for(int i = 0; i < 10; i++) {
        Integer object = new Integer(i);
        stronglyReferenced.add(object);
        REGISTERED.add(new PhantomReference<>(object, QUEUE));
    }
    gcAndPoll("initial");
    stronglyReferenced.removeIf(i -> i%2 == 0);
    gcAndPoll("after removing even");
    stronglyReferenced.clear();
    gcAndPoll("after remove all");
    if(REGISTERED.isEmpty()) System.out.println("all objects collected");
}
static final Field REFERENT;
static {
    try {
        REFERENT = Reference.class.getDeclaredField("referent");
        REFERENT.setAccessible(true);
    } catch (NoSuchFieldException ex) {
        throw new ExceptionInInitializerError(ex);
    }
}
private static void gcAndPoll(String msg)
        throws InterruptedException, IllegalAccessException {
    System.out.println(msg);
    System.gc();
    Thread.sleep(100);
    for(;;) {
        Reference<?> r = QUEUE.poll();
        if(r == null) break;
        Object o = REFERENT.get(r);
        System.out.println("collected (and now resurrected)"+o);
        REGISTERED.remove(r);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM