Consentire la raccolta dei rifiuti di una classe, mentre un anonimo istanza di una classe interna viene fatto riferimento altrove?
-
20-09-2019 - |
Domanda
Ho una classe A:
public class A {
private B b = new B() { public void method() { do something } };
public B getB() { return b; }
}
public interface B { void method(); }
Il b
istanza ha un riferimento implicito dell'istanza della sua classe esterna (che possono fare riferimento this
). Ora un altro oggetto ottiene un riferimento alla presente b
attraverso il metodo getter. Questo b
non può essere garbage collection causa del riferimento.
C'è un modo per ottenere la possibilità di consentire una garbage collection dell'istanza racchiude A
, forse tramite il reset di un esplicito riferimento nella classe interna anonima?
Soluzione
E 'tecnicamente possibile:
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 di ispezione della classe anonima:
Compiled from "HasInner.java"
final class HasInner$1 extends java.lang.Object implements HasInner$
Foo{
final HasInner this$0;
HasInner$1(HasInner);
}
L'implementazione non si basa sul nome del campo essendo this$0
come ho il sospetto che questo è un dettaglio di implementazione del compilatore.
aree potenziale problema:
- Un responsabile della sicurezza può sospendere il codice di riflessione.
- non che la piattaforma Java definisce esattamente come il tipo interno si riferisce a quello esterno. Cioè, si tratta di un dettaglio di implementazione del compilatore e sarebbe legale, se stupido, di avere un involucro intermediario nel campo -. In presenza di altri campi, disambiguare il riferimento può essere impossibile
In breve, vorrei mai fare questo .
Se questo è un problema, utilizzare una classe interna private static :
public class A {
private static class BImpl implements B {
@Override public void method() {
}
}
private final B b = new BImpl();
public B getB() { return b; }
}
Altri suggerimenti
Dopo le chiamate di codice B ref = (new A()).getB()
mucchio Java conterrà un ref
variabile che indicano lo stesso oggetto anonimo come (new A()).b
che a sua volta ha un riferimento interno al suo new A()
racchiude. Non esistono altri riferimenti all'oggetto new A()
.
La tua domanda è, come possiamo forzare una garbage collection di un oggetto, mantenendo vivo l'oggetto B anonimo? La risposta è che non è possibile, perché se si potesse, cosa sarebbe successo con il codice in B che utilizza il riferimento interno di un?
Se si sa che il codice della classe B non fa riferimento la sua classe di inclusione, è possibile dichiarare che statica, il che significa che non riceverà un riferimento interno alla sua classe enclusing. Per fare questo è necessario per rendere una classe annidata, come non si può spaecify classi anonime essere statico:
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(); }
Se il codice chiama B ref = (new A()).getB()
in questa situazione l'oggetto new A()
sarà disponibile per la garbage collection alcun riferimento ad esso esiste.