为什么';Java垃圾收集器不收集垃圾吗?

为什么';Java垃圾收集器不收集垃圾吗?,java,garbage-collection,Java,Garbage Collection,我正在玩一个可怕的数据结构,它基本上是一个树,每个节点都在HashMap对象中存储对其子节点的引用。每当我需要通过将后一个子树设置为新根来清除根及其所有子树(只有一个子树除外)时,我都很难释放内存。我想这可能是我的数据结构中的某个bug,可能是我忘记在那里的某个引用,所以没有任何东西可以进行垃圾收集。但我想先尝试更简单的方法,并实现了以下测试: import java.io.BufferedReader; import java.io.InputStreamReader; import java

我正在玩一个可怕的数据结构,它基本上是一个树,每个节点都在
HashMap
对象中存储对其子节点的引用。每当我需要通过将后一个子树设置为新根来清除根及其所有子树(只有一个子树除外)时,我都很难释放内存。我想这可能是我的数据结构中的某个bug,可能是我忘记在那里的某个引用,所以没有任何东西可以进行垃圾收集。但我想先尝试更简单的方法,并实现了以下测试:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class MyNode {
        MyNode next;
        int somedata;

        public MyNode(MyNode n) {
            next = n;
            somedata = 0;
        }

        public static void main(String[] args) throws IOException {
            MyNode p = new MyNode(null);
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            for (int i=0; i<10000000; i++) {
                MyNode n = new MyNode(p);
                p = n;
            }
            while (p!=null) {
                MyNode p1 = p.next;
                p.next = null;
                p = p1;
            }
            in.readLine();
        }
}

而不是while循环。但是它不起作用,所以我想出了前面的代码。

您可以使用该代码手动调用10000次循环中的垃圾收集

System.gc();

但是也有一些副作用,例如垃圾收集器使用额外的cpu时间。

垃圾收集器的行为很复杂,不同的垃圾收集器可以使用根本不同的方法。你不能期望垃圾被立即回收,即使你明确地说
调用它。

我以前见过这个

htop不是JVM衡量内存利用率的最佳方法。它一直显示出很高的分数。 VM还喜欢将堆分配保持在所看到的高度

我建议使用visualgc或jconsole跟踪JVM附带的jps和jpstat。

另外,您有一个快速创建大量对象的循环,因此gc现在可能有时间启动。 gc不能保证实际执行。这是一个暗示,但通常是有效的

另请看这篇关于Java堆利用率的文章:
你的问题有些不清楚-你真的摆脱了记忆错误吗?你想解决什么问题?您看到测试用例中表现出的行为有几个原因:

  • 当某些东西不再可以从GC根目录访问时,垃圾就不会被收集——它只是变得有资格进行垃圾收集。GC通常仅在分配失败时触发。因为您实际上没有在循环中分配更多的内存来清空引用,所以完全有可能GC还没有运行

  • 即使在收集垃圾时,堆中的内存通常也不会返回到操作系统,因此从操作系统的角度来看它也不会得到准确的答案。使用VisualVM或像jmap和jhat这样的工具将是找出堆上仍然存在什么的最佳方法


  • 小心-分配给进程的内存量与用于对象的内存量不同。大多数进程在分配给操作系统后不会将内存返回操作系统,因此即使垃圾收集器确实运行了,您也不一定会看到它通过分配给进程的总内存反映出来。在Java命令行应用选项
    -XX:+useConMarkSweepGC
    ,以使用并发收集器。否则,我认为HotSpot只会在分配时触发集合。使用HTOP来衡量java任务的内存使用情况肯定是错误的,您需要使用探查器-如果您使用JDK 1.6或更高版本,那么VisualVM会随JDKWell一起提供,我使用HTOP是因为我对程序使用的实际内存感兴趣。但我想我明白了,也许Java不是完成这种alloc/free作业的最佳工具,如果您事先知道需要多少内存,那么只需将Java的最大堆限制在该大小即可。这将迫使GC更加雄心勃勃地回收内存。如果堆上有可回收的内存,您完全可以肯定GC不会允许出现内存不足的情况。这实际上是不推荐的。您可以用它暂停整个VM几秒钟。实际上,JVM决定何时为这个调用提供服务。垃圾收集器在被调用时被调用不是强制性的。所以从某种意义上说它是安全的。但我同意这是一种糟糕的编程实践。@user1559260这不是强制性的,但除非您指定
    -XX:+ExplicitGCInvokesConcurrent
    ,否则HotSpot会在调用
    gc()
    @user1559260时停止所有线程。您错了。Javadoc明确指出,“当控件从方法调用返回时,虚拟机已尽最大努力回收所有丢弃的对象。”这是一个同步调用。你的意思是当我在while循环中将所有引用设置为null时?我在循环结束时尝试调用System.gc()一次。没有什么。如果我每10公里循环一次,它为什么会有什么作用?
    System.gc();