如何解决Java /弱引用:弱引用.get不为null,而没有其他更强的引用将其保存
这是我非常简单的代码:
class UpdateAlertActivity extends Activity {
O o ;
WeakReference<O> _o ;
ArrayList<O> arr = new ArrayList<>();
static class O {
private String l ;
public O(String l) {
this.l = l ;
}
}
void test()
{
arr.clear();
Runtime.getRuntime().gc();
Log.i(LOG_TAG,"breakpoint");
}
@Override
protected void onResume()
{
super.onResume();
o = new O("hello");
arr.add(o);
_o = new WeakReference<>(o);
test();
}
}
当我在test()
方法中清除数组时,我在断点处将_o.get()
设为null
,但事实并非如此。当我在debbuger中看到o
所在的位置时,它仅在弱引用_o
中显示了我,而我认为在没有其他强引用的情况下,会使用弱引用来释放其实例。 ..
我在StackOverflow上看到我错过了调用GC的记录,但是如您所见,调用它在我的程序中没有任何作用。
编辑: 即使这个超级简单的代码也无法正常运行,这也没有道理...
O o = new O("lol");
WeakReference<O> _o = new WeakReference<>(o);
Log.i(LOG_TAG,"1:" + _o.get().toString());
o = null ;
Log.i(LOG_TAG,"2:" + _o.get().toString());
System.gc();
Log.i(LOG_TAG,"3:" + _o.get().toString()); // output not null here
EDIT2: 我想使用GC,因为在另一个静态类中,有一个名为lastUpdates的数组,该数组有时由服务器通过TCP发送的应用程序更新填充。
我的所有活动都观察到该lastUpdate数组(通过实现Observer / Observable接口),并且在对其进行更改时,将通知所有这些事件,并使用新到达的更新列表作为参数调用特定的函数onUpdate(ArrayList u)。此功能仅在恢复活动后才处理更新(活动睡眠期间更改UI会导致应用崩溃)。
如果整个应用程序正在“休眠”(例如,用户在设备菜单中),我希望仅第一个活动醒来才能处理新更新。
为此,我在活动类中创建了一个数组pendingPersistentUpdates
,该数组存储活动睡眠时已捕获的所有更新的弱引用。当第一个活动唤醒时,在应用程序休眠期间捕获的更新仍在lastUpdates数组中,因此我希望存储在pendingPersistentUpdates
活动道具中的弱引用可以返回实际更新,因此我的活动可以对它们进行UI处理,在onResume
。
我期待着这种情况:
- 活动A跑步,活动B暂停(例如,在活动A后面)
- 应用收到更新U。所有活动(A和B)都将得到通知。因为正在运行更新,所以按预期处理更新。 B已将此更新存储在其
pendingPersistentUpdates
中,因为它已暂停。 - 暂停后,用户返回到B。在暂停时,将清除lastUpdates数组,因此B
pendingPersistentUpdates
中更新的弱引用将返回null - B恢复,但是
pendingPersistentUpdates
个弱引用为空(lastUpdates已被清除),因此未处理U。
但是,由于lastUpdate.clear()不会触发GC,因此B pendingPersistentUpdates
个更新仍然存在,并且需要第二次处理(不希望有这种行为)
解决方法
不可能强制垃圾收集器运行。 Runtime.getRuntime().gc()
只是System.gc()
的别名,两者都只是一个提示(请阅读文档;他们提到了这一点)。特别是,调用gc()
通常意味着该收集将在其他某个线程中进行;因此,它更是收集者的“开始标志”; gc()不必暂停并等待gc完成一个完整的周期,它只是告诉gc启动一个。同样,尽管不能保证,Thread.sleep(10000L);
使得您更有可能看到GC调用的效果。
WeakReference
仅保证此对象的存在不会妨碍它引用的对象的任何垃圾回收。就这样。当唯一通过WeakReference
对象到达目标对象的方法时,它不会立即丢失其目标对象。这将在以后发生(实际上,发生在对象被GCed之前 :首先,收集器将清除所有weakref,而稍后,某个对象所占用的内存将真正可以自由使用) 。因此,您在调试器中观察到的内容(唯一的途径就是通过该WR来访问该对象)并不是天生的。
还请注意,调试器本身可能是问题。仅仅在那里并观察它就会产生效果(因此请确保您也不要在运行它)。
如果您希望GC更加努力,则可以分配一些相当大的数组并暂停线程,这会有所帮助,但是您无法做任何保证保证 GC的运行。如果要观察,请使用java -verbose:gc restOfArgsHere
运行Java,并注意控制台。如果确实发生了GC,您将看到它。
但是。
您粘贴的代码甚至无法编译,这表明您要么只粘贴了一半,要么“为了清楚”对其进行了编辑(通常是一个坏主意,最好精确粘贴要使用的内容!)。特别是,您编写了o = new O("hello");
,但是o
并未在任何地方定义。 如果这是您课堂上的一个字段,则意味着完全不能收集该参照对象!所以您可能需要检查一下。使那个O o = new O("hello");
而不是你所拥有的。假设您正确阅读了调试器,并且调试器运行正常,不是吗,但是很可疑。
仅向垃圾回收器建议向系统调用,以释放内存,但不会显式强制使用它。这意味着如果有足够的内存,收集实际上可能不会发生。
https://developer.android.com/reference/java/lang/System#gc()
调用gc方法表明Java虚拟机在回收未使用的对象上花费了很多精力,以便使它们当前占用的内存可供快速重用。当控件从方法调用返回时,Java虚拟机将尽最大努力从所有丢弃的对象中回收空间。
请注意下一个句子“ gc方法表明Java虚拟机会花费更多精力来回收未使用的对象”
内部,垃圾收集器使用启发式/阈值来决定何时收集未使用的对象,因此,基本上,JVM何时需要内存。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。