我创建了一个名为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();
}
答案 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的操作?我认为是的,因为它太奇怪了。因此,您无法从action
到foo
获得强有力的参考。这意味着没有lambdas(如果我理解正确的话)。从foo
到action
的强大参考可以,但您似乎并不希望将它们存储在那里。
我想,虽然有点复杂,但它很可行:
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是静态的,因此只要类是静态的,就被引用。