我刚刚遇到了这个问题。我尝试了几种方法,但它们都不起作用。
通常我有一个StringHolder,可以分配一个字符串,具有set()和listen()方法。
我还有一个IntReceiver,其中包含它接收到的整数,具有set(value)和get()方法。
典型的用例是:
StringHolder sh= new StringHolder();
IntReceiver ir = new IntReceiver();
sh.listen(str->ir.set(str.length());
sh.set("123");
assert(ir.get()== 3);
我尝试了几种实现方式,但它们都不能简单地工作。我尝试了双重弱引用,还尝试了其他一些我甚至都记不起来的东西。
唯一可行的方法是强制接收器保持对lambda的引用。因此,我需要一个新的接口RefStore,其中包含store(Object ref)方法,并使IntReceiver实现它;实现只是一个链表。
然后,listen()需要将RefStore作为参数,并将lambda存储在refstore中。使用代码变成:
sh.listen(str->ir.set(str.length(), ir)
然后你的lambda表达式可以安全地存储在弱引用中,在设置新值并且引用为空时将其删除。
这是我的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);
}
}
这里是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,它就会被回收。问题在于必须调用set()方法来检查空引用并将其删除。
主要问题是这种使其工作的方式需要大量调整类。特别是如果您想让lambda执行某些操作,例如syserr,而不存储数据,则会出现问题。最好能够将lambda附加到对象上,以便将lambda视为强引用。
我还需要尝试的另一件事是,在weakReference的子类中使用finalize来避免实际上在另一个weak reference实际上被GC之前完成引用的最终处理。
编辑:我在主函数中添加了几个测试,通过使用print添加两个监听器之一,其中一个还使用参数(动态),另一个则不使用参数(静态):
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 -> {
});
}
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是静态的,所以只要类存在,就会被引用。
foo
已经被垃圾回收,那么action
永远不会被调用,因为它引用了foo
的一个实例方法。保留action
但让foo
被垃圾回收就像是在说:“我想保留一个ArrayList,但让它的所有项都被垃圾回收。”这样做是没有意义的。 - David Conrad