Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 lambda weak reference

I create an object called Foo. When I create a lambda or method reference called Action, the Action object holds a reference to Foo.
I pass the action to another class. But if I hold it as a weak reference, it gets gc immediately, because no one stores another reference to Action.
But if i hold it as a strong reference, the Foo can't be gc, because Action holds a referene to it.
So memory leaks happen and I want to prevent it.

My question is: how can I hold a reference to Action without preventing gc of Foo.

Example:

Interface Action {
    void invoke();
}

Class Foo() {
    public void someMethod() {
        ....
    }
}

Class Holder() {
     WeakRefrence<Object> foo;
     public Action action;

     void register(Object source, Action a) {
         foo = new WeakReference(source);
         ??? how can i hold the action without prevent source to gc able.
     }
}


main() {
    Holder holder = new Holder();
    Foo foo = new Foo();
    Action action = foo::someMethod;

    holder.register(foo,action);
    action = null;
    System.gc();
    //only the holder store reference to action.
    //if i store in holder as weak reference i cannot invoke it any more cause it get gc.

    //foo = null;
    //System.gc();
    //if i grab action in holder as strong refrence the foo cant be gc cause action hold refernce to it.

    holder.action.invoke();
}
like image 216
Lege Avatar asked Apr 01 '14 07:04

Lege


1 Answers

You have to separate the action from the weakly referenced target. You should always keep in mind that lambdas are intended to specify behavior only.

class Foo {
    public void someMethod() {
       System.out.println("Foo.someMethod called");
       // ....
    }
}

class Holder<T> extends WeakReference<T> {
    private final Consumer<T> action;
    Holder(Consumer<T> action, T target) {
        super(target);
        this.action=action;
    }
    public void performAction() {
        T t=get();
        if(t!=null) action.accept(t);
        else System.out.println("target collected");
    }
}

class Test {
  public static void main(String... arg) {
    Foo foo = new Foo();
    Holder<Foo> holder = new Holder<>(Foo::someMethod, foo);
    System.gc(); // we are still referencing foo
    holder.performAction();
    foo = null;
    System.gc(); // now we have no reference to foo
    holder.performAction();
  }
}
like image 197
Holger Avatar answered Sep 28 '22 10:09

Holger