2010-01-13 20 views
4

我有一個A類:在其他地方引用匿名內部類實例時允許垃圾回收類嗎?

public class A { 
    private B b = new B() { public void method() { do something } }; 

    public B getB() { return b; } 
} 

public interface B { void method(); } 

實例b其外類的實例的一個隱式引用(可通過this引用)。現在,另一個對象通過getter方法獲得對此的引用b。由於參考,此b不能被垃圾收集。

有沒有辦法讓封閉A實例的垃圾回收的可能性,也許通過重置匿名內部類中的顯式引用?

回答

3

這在技術上是可行的:

public class HasInner { 
    public static interface Foo {} 

    private static <T> T release(T instance, Object ref) { 
    try { 
     Class<?> type = instance.getClass(); 
     for (Field field : type.getFields()) { 
     if (!field.isAccessible()) { 
      field.setAccessible(true); 
     } 
     if (field.get(instance) == ref) { 
      field.set(instance, null); 
     } 
     } 
    } catch (IllegalAccessException e) { 
     throw new IllegalStateException(e); 
    } 
    return instance; 
    } 

    public Foo makeFoo() { 
    return release(new Foo() {}, this); 
    } 

    public static void main(String[] args) { 
    new HasInner().makeFoo(); 
    } 
} 

匿名類的的javap檢查:

Compiled from "HasInner.java" 
final class HasInner$1 extends java.lang.Object implements HasInner$ 
Foo{ 
    final HasInner this$0; 
    HasInner$1(HasInner); 
} 

實現不依賴於字段名稱爲this$0,我懷疑這是一個編譯器實現細節。

可能存在的問題:

  • 安全管理器可以禁止反射代碼。
  • 我不認爲認爲 Java平臺明確定義內部類型是如何引用外部的。也就是說,這是一個編譯器實現細節,如果在域中有一箇中間包裝器(如果存在其他領域的話)可能是合法的,但是消除歧義引用可能是不可能的。

總之,我會永遠不要這樣做

如果這是一個問題,使用私有靜態內部類

public class A { 
    private static class BImpl implements B { 
    @Override public void method() { 
    } 
    } 

    private final B b = new BImpl(); 

    public B getB() { return b; } 
} 
+0

添加此評論,以便我可以看到OP是否最終接受此黑客,無論您的意見如何:-)(並且不,如果您得到低投訴,這不是我的;雖然我希望你刪除答案) – kdgregory 2010-01-13 13:22:22

+0

Thx爲答案。很明顯,靜態內部類是更好的選擇。 不,我永遠也不會使用這個選項。你可以添加一些'壞'標記... – boutta 2010-01-13 13:39:35

+0

@kdgregory:我只想知道一種可能性,而不是使用它。是的,這個答案就是我正在尋找的。 – boutta 2010-01-13 13:42:24

3

你的代碼後調用B ref = (new A()).getB() Java堆將包含變量ref其指向同一個匿名對象爲(new A()).b這轉了一個內部引用到它的封閉new A()。沒有其他對new A()對象的引用存在。

你的問題是,我們如何強制A對象的垃圾回收,同時保持匿名b對象存活?答案是你不能,因爲如果可以的話,b中使用內部引用A的代碼會發生什麼?

如果你知道你的B類的代碼沒有引用它的封閉類,你可以聲明它是靜態的,這意味着它不會收到它的enclusing類的內部引用。要做到這一點,你需要使它成爲一個嵌套類,因爲你不能spaecify匿名類是靜態的:

public class A { 
    static class Bimpl implements B { public void method() { do something } }; 

    private B b = new Bimpl(); 

    public B getB() { return b; } 
} 

public interface B { void method(); } 

如果你的代碼在這種情況下調用B ref = (new A()).getB()new A()對象將可用於垃圾收集,因爲沒有參考它存在。

+0

+1 - 我相信FindBugs會告訴你,如果你有一個可以嵌套的內部類(我知道它會做到這一點與實例/靜態方法) – kdgregory 2010-01-13 13:24:28

+0

得像FindBugs:SIC:應該是一個靜態內部類:http://findbugs.sourceforge.net/bugDescriptions.html#SIC_INNER_SHOULD_BE_STATIC – trashgod 2010-01-13 15:22:57