Java 8 lambda弱引用

时间:2014-04-01 07:28:57

标签: java lambda garbage-collection java-8 weak-references

我创建了一个名为Foo的对象。当我创建名为Action的lambda或方法引用时,Action对象保存对Foo的引用。
我将动作传递给另一个班级。但如果我把它作为一个弱引用,它会立即得到gc,因为没有人存储另一个Action的引用。
但是如果我把它作为一个强大的参考,那么Foo就不能是gc,因为Action对它有一定的参考。
所以内存泄漏发生了,我想阻止它。

我的问题是:如何在不阻止Action的gc的情况下保留对Foo的引用。

示例:

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();
}

4 个答案:

答案 0 :(得分:2)

您必须将操作与弱引用的目标分开。您应该始终记住,lambdas仅用于指定行为

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();
  }
}

答案 1 :(得分:0)

反向策略是将所有动作注册到Foo。这似乎不适合。

为什么不SoftReference?然后行动必须检查,但这似乎是你的意图。

您还可以在foo(addListener,反向策略)中注册Action,以指示状态更改为“dying”。

答案 2 :(得分:0)

  

我的问题是:如何在不阻止Fc的情况下保留对Action的引用。

我说首先需要澄清一些事情:

  • action应该知道应该调用哪个Foo个实例吗?我这么认为,否则它很简单。
  • action是否应阻止其存储的foo来自GC?我认为不是,因为这就是你所要求的。
  • foo是否应阻止其对GC的操作?我认为是的,因为它太奇怪了。

因此,您无法从actionfoo获得强有力的参考。这意味着没有lambdas(如果我理解正确的话)。从fooaction的强大参考可以,但您似乎并不希望将它们存储在那里。

我想,虽然有点复杂,但它很可行:

  • action存储WeakReference<Foo>
  • 除非您希望尽可能长时间地保留SoftReference,否则不要使用foo
  • 在使用foo执行任何操作之前,请检查get是否已返回null
  • holder中运行一些清理工作,以摆脱Actions丢失Foo的问题(ReferenceQueue是您的朋友)。

答案 3 :(得分:0)

我刚才偶然发现了这个问题。我尝试了几件事,但它们不起作用。

通常,我有一个StringHolder,可以使用set()和listen()方法为其分配一个String。 我还有一个IntReceiver,其中包含使用方法set(value)和get()接收到的int。

典型的用例是:

StringHolder sh= new StringHolder();
IntReceiver ir = new IntReceiver();
sh.listen(str->ir.set(str.length());//should test null
sh.set("123");
assert(ir.get()== 3);

我尝试了几种实现。他们不能简单地工作。我尝试了双重弱引用,我尝试了其他我什至不记得的事情。

唯一的工作方法是强制接收器保留对lambda的引用。因此,我需要一个带有store(Object ref)方法的新接口RefStore,并让IntReceiver实现它;该实现只是一个链表。

然后,listen()需要将RefStore作为参数,并将lambda存储在refstore中。使用代码变为

sh.listen(str->ir.set(str.length(), ir);

然后,您的lambda可以安全地存储在弱引用中,在设置新值且ref为null时将其删除。

这是我的StringHolder

public class StringHolder{

    private List<WeakReference<Consumer<String>>> listeners = new ArrayList<>();

    public int listeners() {
        return listeners.size();
    }

    public void listen(Consumer<String> listener, RefStore holder) {
        holder.store(listener);
        listeners.add(new WeakReference<>(listener));
    }

    public void set(String data) {
        for (Iterator<WeakReference<Consumer<String>>> it = listeners.iterator(); it.hasNext();) {
            Consumer<String> call = it.next().get();
            if (call == null) {
                it.remove();
            } else {
                call.accept(data);
            }
        }
    }

}

这是我的IntReceiver

public class IntReceiver implements RefStore {

    private int value;

    private transient LinkedList<Object> stored = new LinkedList<>();

    @Override
    public void store(Object o) {
        stored.add(o);
    }

    public void set(int val) {
        value = val;
    }

    public int get() {
        return value;
    }

}

这是主要的:

public class MainExample {

    public static void main(String[] args) {
        IntReceiver ir = new IntReceiver();
        StringHolder sr = new StringHolder();
        sr.listen(s -> ir.set(s.length()), ir);

        sr.set("123");
        System.out.println("value for 123 is " + ir.get());
        System.out.println("calls=" + sr.listeners());
        add(sr);
        sr.set("1234");
        System.out.println("value for 1234 is " + ir.get());
        System.out.println("calls=" + sr.listeners());
        GCManage.force();
        sr.set("132");
        System.out.println("value for 132 is " + ir.get());
        System.out.println("calls=" + sr.listeners());

    }

    protected static void add(StringHolder ls) {
        IntReceiver is = new IntReceiver();
        ls.listen(s -> is.set(s.length()), is);
    }

}

这是强制GC的force()方法。

public static void force() {
    WeakReference<Object> wr = new WeakReference<>(new Object());
    int nb = 0;
    int arraySize = (int) (Math.min(Runtime.getRuntime().freeMemory() / 32 / 8, Integer.MAX_VALUE - 5) / 10);
    while (wr.get() != null) {
        @SuppressWarnings("unused")
        Object[] trash = new Object[arraySize];
        System.gc();
        nb++;
    }
    logger.debug("gc after " + nb + " buffers");
}

以下是输出:

value for 123 is 3
calls=1
value for 1234 is 4
calls=2
value for 132 is 3
calls=1
如您所见,

在对add()的调用之后,添加了一个侦听器,但尚未添加gc;曾经强制使用GC,但是它已被收集。问题在于必须调用set()方法检查空引用并将其删除。

主要问题是,这种使之起作用的方法需要对类进行大量调整。如果您想让lambda在不存储数据的情况下执行某些操作,例如syserr,则尤其成问题。 最好能够将lambda附加到对象上,以便lambda被认为是强引用的。

我还需要尝试的另一件事是,尝试在weakReference的子类中使用finalize,以避免实际完成对ref的最终确定,直到实际对另一个弱引用进行了GC处理。

edit:我主要通过使用print添加到侦听器中添加了一些测试,其中一个也使用parameter(动态),另一个不使用它(静态):

public class MainExample {

    public static void main(String[] args) {
        IntReceiver ir = new IntReceiver();
        StringHolder sr = new StringHolder();
        sr.listen(s -> ir.set(s.length()), ir);

        sr.set("123");
        System.out.println(" value for 123 is " + ir.get());
        System.out.println(" calls=" + sr.listeners());
        System.out.println("adding weak referenced listeners");
        add(sr);
        addSysoDynamic(sr);
        addSysoStatic(sr);
        sr.set("1234");
        System.out.println(" value for 1234 is " + ir.get());
        System.out.println(" calls=" + sr.listeners());
        System.out.println("force GC");
        GCManage.force();
        sr.set("132");
        System.out.println(" value for 132 is " + ir.get());
        System.out.println(" calls=" + sr.listeners());

    }

    protected static void add(StringHolder ls) {
        IntReceiver is = new IntReceiver();
        ls.listen(s -> is.set(s.length()), is);
    }

    protected static void addSysoDynamic(StringHolder ls) {
        ls.listen(s -> System.out.println(" received(dynamic) " + s + " from " + ls.getClass().getSimpleName()), o -> {
        });
    }

    // no ref to the argument means the lambda is static and linked to the class
    protected static void addSysoStatic(StringHolder ls) {
        ls.listen(s -> System.out.println(" received(static) " + s), o -> {
        });
    }

}

这是结果:

 value for 123 is 3
 calls=1
adding weak referenced listeners
 received(dynamic) 1234 from StringHolder
 received(static) 1234
 value for 1234 is 4
 calls=4
force GC
 received(static) 132
 value for 132 is 3
 calls=2

如您所见,动态侦听器是GC,但不是静态侦听器,因为lambda是静态的,因此只要类是静态的,就被引用。