3

在谷歌代码上找到的一些库中,我遇到了这个 util 方法:

public static void gc(){
    Object obj = new Object();
    WeakReference ref = new WeakReference<Object>(obj);
    obj = null;
    while(ref.get()!=null)
    System.gc();
}

它的文档说它提供了一种调用 GC 的可靠方法,因为调用 System#gc() 只是一个提示,没有任何保证。我给我的学长看,他说我应该想想为什么这个方法无效。我读了一些关于弱引用的文章,但我仍然感到困惑。有人可以告诉我重点吗?

4

6 回答 6

7

我对您发布的所谓的“安全 GC”成语有直接的经验。

它不起作用。

原因很简单:只是一个weak ref被清除的事实并不是对象已经被收集的信号;它仅意味着它已通过任何强引用或软引用变得无法访问。以我的经验,这个信号在对象被回收之前到达。

更好的尝试是使用 Phantom 引用,这至少可以确保对象已进入可终结状态,但同样,它仍然可以占用堆,实际上它仍然占用它。另一种解释可能是,这个明显位于 Eden 空间中的特定对象确实被回收了,但是执行它的 GC 运行并不详尽,并且在其他代中有更多的内存需要回收。

另一方面,我非常可靠地使用了这个琐碎的成语:

for (int i = 0; i < 3; i++) { System.gc(); Thread.sleep(500); }

试试看它是否适合你。该部分是可选的:仅在使用并发扫描sleep时才需要。System.gc()

如果您反对这种方法明显的反复无常,请记住任何显式 GC-ing 的方法都是反复无常的。这个至少是诚实的——并且恰好在实际系统上工作。它自然是不可移植的,并且可能由于多种原因随时停止工作。即便如此,它仍然是你能得到的最好的。

于 2013-09-26T14:33:54.933 回答
2

关键是,System.gc()不需要清理所有弱引用。并考虑一些 Java 虚拟机。如果System.gc一次(第一次)确实决定清除该引用,则很可能是下一次调用。因此,您可能有一个无限循环。可能取决于其他线程更改垃圾收集的状态以终止循环。

所以:一次就够了。

于 2013-09-26T14:30:23.683 回答
1

没有办法保证GC 调用,因为正如文档所说System.gc,这只是系统可以忽略的提示。

所以假设JVM忽略了System.gc——在这种情况下,整个事情只是循环,直到系统的其他部分导致GC。如果您运行单线程或没有其他人分配大量内存,您基本上会在此处创建一个无限循环。

于 2013-09-26T14:30:39.847 回答
0

关键是您的线程将停止并等待直到弱引用被清除,从而“模拟”垃圾收集。无法保证何时(甚至如果)这会真正发生。

您可能会在此等待while很长时间。

于 2013-09-26T14:31:02.923 回答
0

编程我们需要确保,当一个 obj 被删除时,它的相应条目应该被删除。只有这样,该对象才会成为垃圾回收的候选对象。否则,即使它没有在运行时使用,这个过时的对象也不会被垃圾回收。

此引用所引用的对象,如果此引用对象已被清除,则为 null。作为 WeakReference 类的对象引用。所以它不会给null。但删除 gc. 后,它提供 null。

 Object obj = new Object();
         WeakReference ref = new WeakReference<Object>(obj);
         obj = null;
         if(ref.get()!=null)
         {
             System.gc();
             System.out.println("remove ref");
         }
         if(ref.get()!=null){
             System.out.println("not execute"); 
         }

输出:

remove ref

不要将空值分配给 obj。

 Object obj = new Object();
         WeakReference ref = new WeakReference<Object>(obj);
         if(ref.get()!=null)
         {
             System.gc();
             System.out.println("remove ref");
         }
         if(ref.get()!=null){
             System.out.println("execute"); 
         }

输出:

remove ref
execute
于 2013-09-26T14:38:19.617 回答
0

试图强制 GC 的代码通常预示着潜在的更大问题(即设计问题或开发人员缺少知识)。

我已经看到了一些在生产代码中调用System.gc()实际上是有意义的用例,例如,在打印当前内存使用之前 - 值是否关闭并不重要,但我们希望提高值的可能性可能的。当然,我们知道 GC 已启用——我们使用它来自动检测 QA 系统上的内存泄漏。

一般来说,调用System.gc()会大喊“我的代码有问题,我不知道如何修复它!”。

于 2013-09-26T14:49:12.367 回答