显式调用System.gc()有必要吗?

17
据说在Java中我们无法强制进行垃圾回收进程。
毕竟,它是一个守护线程。但是有时候,我们为什么还要显式地调用System.gc( )函数呢?是否值得调用?有哪些优缺点?如果在许多情况下都没有用处,为什么这个方法没有被Java废弃呢?
备注:通过示例进行解释会更有用。
8个回答

10
我认为,最好的方式是将 System.gc() 方法视为提示虚拟机运行垃圾收集。话虽如此,就像很多人认为他们在执行的“优化”一样,通常最好让系统自己处理这些事情。系统正在不断发展等等。仍然有一些情况下,开发者可能真的知道得更好,使用它的用例可能非常类似于为什么仍然会编写汇编代码(大多数情况下,编译器更好,但在某些情况下——或对于某些开发者来说——人类实际上可以编写更高效的代码)。
过去我看到过的一个例子是,如果分配了大量对象,并且您作为开发者知道它们不再使用的时刻,在这种情况下,您可能比GC具有更多关于内存利用的信息(或者至少在它意识到之前),并且由于所回收的内存量相当可观,因此建议运行垃圾收集是有意义的。

8
你回答了你问题的一半:它值得吗?不,因为你不能强制它。
运行垃圾收集器。调用此方法表明Java虚拟机会花费力气回收未使用的对象,以使它们当前占用的内存可用于快速重用。
这几乎是EULA-英语,意思是“你可以尝试,但我们都知道结果”。 :)
此外,应用程序服务器可以(并经常会)使用-XX:-DisableExplicitGC命令行选项禁用它。
那么,这有什么意义呢?好吧,可能有一个:一些应用程序喜欢显示堆上可用的空闲内存量,在刷新显示之前,调用System.gc();可能会有些用处。

System.gc()虽然是一个昂贵的调用。"当从方法调用返回控制时,Java虚拟机已经尽最大努力从所有废弃对象中回收空间"。 - RecursiveExceptionException
1
这就像说健康的生活方式不值得尝试,因为它不能保证有效。如果你控制环境,你可以确保它有效。System.gc() 的误用远比正确使用要多得多,但这并不意味着它没有用处。 - maaartinus

7

问:为什么我们需要显式地调用System.gc()函数?

答:因为有些人写了糟糕的代码。

我认为大多数人都会同意,你不应该显式地调用System.gc()

让系统按照它应该的方式管理内存。任何依赖此操作以提高性能的代码很可能是错误的。另外,请记住JVM完全可以忽略您的请求。

如果您想要更多信息,建议阅读以下答案:

java - 为什么调用System.gc是一种不好的做法?


是的,这就是HFJ建议的。但我并不完全相信他们的答案。 - Saurabh Gokhale

4

在某些情况下调用System.gc()是有用的:

  • 您将运行一些基准测试,因此需要从良好定义的状态开始。
  • 您预计会有一些重负荷,并希望尽可能做好准备。
  • 您有多个服务器,并希望通过定期安排GC来避免暂停像这里一样

可能还有更多用例,但这不是您很快就需要的。大多数情况下最好让它按照自己的方式工作。


1
我不同意第二个观点——如果你开始运行“重”负载,那么通常JVM足够智能地意识到这一点,并会进行适当的垃圾收集。 - Michael Berry
我的意思是,在你开始重负载之前就要做好准备,这是JVM事先无法知道的。就像在你预料到会有一个艰难的一天时提前完成所有任务一样。 - maaartinus
@berry120 如果你确定要这么做,你可以清空Sun的热点中的young gen。通常,在多线程环境中调用GC是不推荐的,除非修复Java开发人员留下的问题。 (再次谈论可恶的内存映射缓冲区) - bestsss
为了获得“最佳结果”,我发现需要连续调用 System.gc() 函数多次(可能需要 10 次)。此外,我发现暂停一下(Thread.sleep,大约 1 秒)有助于获得更一致的 CPU 密集型基准测试结果,因为这可以让 CPU 冷却一下。同时,这个暂停也有助于确保 System.gc() 实际上正在释放一些空间。 - Thomas Mueller

2

不打算回答这个问题,显式调用垃圾收集器是一种非常糟糕的做法,但是Java中的某些代码确实依赖于finalization...并试图强制执行它。在某些分析场景下调用它也是有用的。没有标准的JVM会忽略System.gc()。当然,它可以被禁用。然而,由于垃圾收集器管理Java引用到外部资源,因此可能需要它。

以下是来自java.nio.Bits的一些代码:

static void reserveMemory(long size) {

    synchronized (Bits.class) {
        if (!memoryLimitSet && VM.isBooted()) {
            maxMemory = VM.maxDirectMemory();
            memoryLimitSet = true;
        }
        if (size <= maxMemory - reservedMemory) {
            reservedMemory += size;
            return;
        }
    }

    System.gc();
    try {
        Thread.sleep(100);
    } catch (InterruptedException x) {
        // Restore interrupt status
        Thread.currentThread().interrupt();
    }
    synchronized (Bits.class) {
        if (reservedMemory + size > maxMemory)
            throw new OutOfMemoryError("Direct buffer memory");
        reservedMemory += size;
    }

睡眠对于程序的正常运行是必要的(或尝试运行),清理器通常可以在高优先级的finalizer线程上快速执行。


0
这里是关于Java的哪个版本?在6中,我从未明确地调用过它。一般来说,垃圾回收器足够聪明,知道何时清理资源,并且具有足够的可配置VM选项。我从未觉得需要在代码中调用它,除非你正在做一些非常奇怪的事情(或者显示资源使用情况),最好不要这样做。如果你感觉需要调用它,那么我会说你在其他地方做错了什么。
但是,如果我们谈论的是与Java 1.4或之前的版本一起工作,有时我发现它确实需要帮助。没有手头的例子(抱歉),但我记得需要给它一个提示,以避免它最终启动时出现可怕的延迟。在6上使用相同的代码,问题消失了,调用它几乎没有任何区别。
当然,当你调用System.gc()时,你实际上只是建议VM现在可能是运行垃圾回收器的好时机。这并不意味着它实际上会这样做,它只是一个建议,完全有效的VM实现可能完全忽略它。实际上,甚至有一个DisableExplicitGC选项,意味着这些调用绝对不会生效(许多生产环境中的代码都是使用此选项运行的)。

所以是的,在编程中仍然使用它 - 但绝大多数情况下这并不是一个好的实践。


0

显式地使用System.gc()是一种反模式,虽然我们使用它来建议JVM进行垃圾清理,但它取决于JVM是否会进行清理,同时还会强制执行一些标准。只有当标准符合要求时,它才会采取行动;否则就不会。


-1
It is said that we cannot force the garbage collection process in java.

这是一个非常流行的观点,但是完全错误。

有几个应用程序定期执行此操作,甚至有一些闪亮的按钮可以点击,确实会执行垃圾回收。换句话说,它们并不是“暗示”垃圾回收器启动垃圾回收,而是真正地强制进行垃圾回收。

因此,这种信念既流行又错误。更不为人知的是如何实际触发这样的垃圾回收,正如我在这里所提问的+6次赞的问题的唯一(且非常模糊)答案所示:

Java: 如何使用JVMTI的ForceGargabeCollection真正强制进行垃圾回收?


5
由于JVM规范中并没有要求垃圾收集器,因此无法“强制”通用JVM进行垃圾回收。 - ILMTitan
1
当方法调用返回控制权时,Java虚拟机已经尽最大努力从所有废弃对象中回收空间。没有垃圾回收的JVM也会尽最大努力,所以没有理由进行负评。 - maaartinus
@ILMTitan:那么我猜99%的Java程序都在支持jvmti仪器化的VM上运行,因此支持该文档调用是件好事;) - SyntaxT3rr0r
3
@SyntaxT3rr0r,根据你的问题中所解释的内容,这个神话就是System.gc()只是一个模糊的提示。尽管规范允许JVM将其视为提示,但在标准的桌面/服务器JVM中,它只是运行它。这就是这些闪亮按钮通常实现的方式。我没有给你的回答点踩,但我想指出你的回答并没有回答问题。它最多只能算是一条评论。 - Yishai
1
我并不是实际下投票的人,因为我不知道那个API,但我确实觉得它很有趣(尽管你可以争辩它不是“在Java中”)。我的评论主要是为了表明这个答案中的修辞比现实支持的要强烈一些。 - ILMTitan
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接