Java -> System.gc(); 这个调用会开启一个新的线程吗?

5
例如,我有这样的代码:

... 获取一些内存并丢失指向该内存的所有指针,以便System.gc();可以收集它。

调用System.gc();

执行其他任务;


"执行其他任务;"和"调用System.gc();"是并行工作还是"执行其他任务;"等待"调用System.gc();"被执行?

谢谢。

9个回答

7
你可能不应该使用System.gc()。C/C++用户转到Java时常见的误解是,他们认为需要告诉虚拟机何时可以执行垃圾回收。实际情况是,垃圾回收器已经高度优化,并在感觉到最佳时自行执行此任务。通常不建议调用System.gc()。
如果确实要调用System.gc(),它会“建议”系统执行垃圾回收。但实际上可能不会执行回收,尽管通常会执行。它是否在另一个线程中运行取决于实际的收集算法。默认算法将在运行时阻塞所有内容。因此,它可能在单独的线程中运行,但在执行时会阻塞当前的执行。

但是,我有这样的限制。-Xmx3000m 首先我得到了2.4g 其次我获得了400m,然后失去了指向那个内存空间的所有指针。 第三,我想立即释放我在第二步中占用的400m,以便我可以获得另外400m 如果我不立即调用gc(); 我就无法再获得400m。我认为这就是我的迭代算法在服务器上停止某些迭代的原因。 - ogzylz
如果这是正确的:那么它可能在单独的线程中运行,但当它运行时,它会阻塞您当前的执行。我很安全。谢谢。 - ogzylz
1
@ogzylz,它不是这样工作的。您不需要调用gc()来释放内存。JVM将继续运行,并可能在执行gc()之前接近3g标记。有了3g可用空间,它不需要经常运行gc()(提高性能)。如果您看到进程消耗接近3g的内存,请不要担心。这是正常的。 - Chris Dail
FYI:是的,你的答案是正确的。我找到了我的错误所在。首先,我使用-Xmx3600m运行了一个程序。然后,我又运行了第二个程序,同样使用了-Xmx3600m。JVM使我能够运行这些程序。但是,32位JVM只能寻址最多4GB的内存。当这两个程序的总内存分配超过4GB时,我很可能会遇到OutOfMemoryError(我不确定是否确实如此,因为我没有正确记录日志)。当我依次运行这些程序时,它们都可以正常工作,没有任何问题。 - ogzylz
这就是为什么我认为System.gc()不像你说的那样起作用,我需要自己调用它。总之,我最好尽快切换到64位机器。 - ogzylz
我意识到之前的解释也是不正确的。我在使用线程和同步方面出了问题。我已经解决了这个问题。最初的问题与使用System.gc()或内存问题无关。 - ogzylz

6
这里的确切行为完全取决于JVM实现。在规范中(也就是一个合适的JVM实现需要提供的内容),它可以并行发生,它可以在您的代码执行之前首先发生,或者根本不会发生。
实际上,在我遇到的JVM上观察到的情况是它立即在单独的线程中运行。但是,在某些情况下,多次调用会生成多个线程,有时它会将请求排队在一个线程上。垃圾收集启动总是一种“停止应用程序”的类型(也就是它非常完整,并且会减慢或暂停应用程序)。
然而,考虑到您对@Chris Dail的评论,您的根本问题不是System.gc()调用的行为。调用System.gc()可能有一些用途。它可以用于清除内存,以便您可以了解应用程序当前实际占用的空间大小。它还可以用作策略,以确保早期启动"停止应用程序"垃圾收集,从而因要清理的内存更少导致"停止应用程序"时间更短。 (我应该指出,随着JVM变得越来越复杂,这种做法越来越不必要,实际上反而是适得其反)。
然而,它不会以任何方式解决OutOfMemoryError。 JVM在尽最大努力进行垃圾收集之前不会给您一个OutOfMemoryError。调用System.gc()并不改变这一点。如果您遇到了OutOfMemoryError,那么很可能是因为您以某种方式持有对象引用,但实际上您并不需要这些引用,但这些引用却阻止该对象的内存被回收。

FYI:是的,你的答案是正确的。我找到了我的错误所在。首先,我使用-Xmx3600m运行了一个程序。然后,我又运行了第二个程序,同样使用了-Xmx3600m。JVM使我能够运行这些程序。但是,32位JVM只能寻址最多4GB的内存。当这两个程序的总内存分配超过4GB时,我很可能会在运行这些程序时遇到OutOfMemoryError(我不确定是否确实是这种情况,因为我没有正确记录日志)。当我依次运行这些程序时,它们都可以正常工作,没有出现问题。 - ogzylz
这就是为什么我认为System.gc()不像你说的那样起作用,我需要自己调用它。总之,我最好尽快切换到64位机器。 - ogzylz
@ogzylz,如果你需要那种内存,你需要一台64位的机器和JVM。然而,这里的限制不仅仅是JVM,还有操作系统。JVM需要连续的地址空间,而32位操作系统很可能无法提供这种内存,而且在给两个需要那种内存的JVM时非常有限。 - Yishai
我意识到之前的解释也是不正确的。我在使用线程和同步方面出了问题。我已经解决了这个问题。最初的问题与使用System.gc()或内存问题无关。 - ogzylz

4

通常情况下,它是同步的,但不保证。

实际上,并不保证集合会有效地发生,因为JVM将决定是否有意义以及使用何种垃圾收集方式。

如果您需要更多信息,可以使用-verbosegc标志启动程序。

无论如何,垃圾回收都是由JVM自动发起的,没有任何指定的调用,调用System.gc()只是一种提示,让它知道它可能开始进行收集。


1
JVM实现决定了垃圾收集的执行方式。通常实现可以停止所有线程以全局收集内存,也可以只停止一个线程以收集该线程的本地内存。可以从Sun和这篇较旧但很好的文章中了解更多关于垃圾回收的信息。

1

正如Eric Eijkelenboom所指出的那样,调用此方法并不意味着垃圾回收器会立即执行。实际上,你不知道它是否会被调用...

根据你对Amarghosh答案的评论,你需要在执行需要大量RAM的操作之前释放一些内存,是吗?在这种情况下,你不需要担心。如果有足够的内存可以进行垃圾回收,当你尝试分配它时,它将自动完成。


是的,你说得对。我意识到使用线程和同步时出了问题。我已经解决了这个问题。最初的问题与使用System.gc()或内存问题无关。 - ogzylz

0

“做一些其他任务”等待System.gc()执行并返回。


我希望你是正确的,因为我这样假设并相应地编写了我的代码。 你确定吗? - ogzylz

0

无法确定。调用System.gc()并不意味着垃圾回收会在那一秒钟内执行,而只是被安排了。由JVM来决定在适当的时间执行垃圾回收。


System.gc()的API文档中可以得知:"当方法调用返回时,Java虚拟机已经尽最大努力回收所有废弃对象所占用的空间。" - Matthew T. Staebler
是的,我也读过那个,但是“尽力而为”是什么意思呢?它会为该进程创建一个新线程吗?还是我的“执行某些任务”要等待System.gc()?这并不清楚。 - ogzylz

0

javadoc 告诉 http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#gc() : "当方法调用返回控制权时,Java虚拟机已经尽最大努力从所有废弃对象中回收空间。"

所以我认为 "do some other tasks;" 等待执行 "System.gc();"!

没有使用并行处理 - 这对于Java应用程序来说不好。事实上,调用/强制垃圾回收器从来都不是一个好主意。它必须保留给极端情况下没有其他解决方案的情况...

再见, 阿尔班。


是的,我有这样一个极端的情况。我会再次重复我的答案:但是,我有这样一个限制。-Xmx3000m首先我得到2.4g,其次我得到400m更多,然后失去了所有指向那个内存空间的指针。第三,我想立即释放我在第二步中占用的400m,以便我可以获得另外400m。如果我不立即调用gc();,我就无法再获得400m。我认为正因为这个原因,我的迭代算法在服务器上停止了一些迭代。 - ogzylz

0
根据Java规范,调用System.gc()的线程会被停止直到GC完成其操作。请注意这只是一个提示,JVM可以自由忽略它,而Sun的JVM实际上有一个命令行选项(-XX:-DisableExplicitGC)以此为例。使用此开关,System.gc()将无效并立即返回。
无论哪种方式,规范中都不会防止其他线程运行。这取决于GC算法;Sun的JVM包括多个算法,其中一些能够与应用程序线程并发运行大部分工作。
正如其他人指出的那样,显式调用System.gc()的净效果在大多数情况下是降低性能。如果您觉得必须调用该方法,则很可能是在做某些错误的事情。

是的,你说得对。我已经认识到我的错误所在。如果想进一步了解我哪里错了,你可以阅读我对Chris Dail答案的回应。谢谢。 - ogzylz

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