强制Java虚拟机运行垃圾回收器

4

我有一个复杂的Java应用程序,它在大型数据集上运行。该应用程序的性能表现相当不错,但随着时间的推移,它似乎会消耗大量内存并变得越来越慢。有没有一种方法可以在不重新启动应用程序的情况下运行JVM垃圾收集器?


4
如果资源变得稀缺,它将自动运行。因此,如果有些资源可以回收利用,您就不需要采取任何措施,或者如果没有资源可用,那么您需要修改设计...... - assylias
1
有什么证据表明内存使用导致了您的性能问题?我并不是说这不是您的问题,但如果您想加快应用程序的速度,您应该确保您正在寻找正确的改进方向。如果您不知道首先失去了哪些性能,那么您可能会浪费很多时间尝试提高性能。短语“随着时间的推移”(2分钟、3小时、4天?)和“似乎消耗了大量内存”(您是否在地板上找到了它的碎片?)让我想知道根本原因是否已经被充分确定。 - arcy
1
有趣的是,这个问题被一个名叫“DotNet”的用户提出了 (-: - Adriaan Koster
8个回答

17
不,你不能强制进行垃圾回收。 即使使用
System.gc();  

你可以向垃圾回收器发出请求,但是否执行取决于JVM。
此外,垃圾回收器足够智能,在需要时会回收未使用的内存,因此不应强制进行垃圾回收,而应检查是否以错误的方式处理对象。 如果以错误的方式处理对象(例如保留对不必要对象的引用),JVM几乎无法释放内存。
来自文档
调用gc方法建议Java虚拟机努力回收未使用的对象,以便将它们当前占用的内存释放出来,以便快速重用。当方法调用返回时,Java虚拟机已尽力从所有丢弃的对象中回收空间。
关于System.gc()文档,请打开错误
System.gc()的文档非常误导人,并且没有提到永远不要调用System.gc()的推荐做法。
语言选择不明确System.gc()被调用时的行为以及外部因素将如何影响行为。
当您认为应该强制JVM释放一些内存时,可以访问一些有用的链接:
1. [垃圾回收是如何工作的](link1) 2. [System.gc()什么时候会起作用](link2) 3. [为什么调用System.gc()是不好的做法](link3)
所有的链接都表明:
1. 在Java中,您无法控制GC,即使是System.gc()也不能保证。 2. 强制调用可能会对性能产生不利影响,因此这是一种不好的做法。 3. 重新审视您的设计,让JVM完成它的工作 :)

为了扩展我的知识,您能否提供一个链接,解释在调用System.gc()时垃圾回收器不执行的情况(除非已明确禁用)? - Neet
是的,API文档中的第一行说:“运行垃圾回收器。” - Neet
@Neet:我已更新我的回答,如果你继续阅读,它会建议并尽最大努力。 - Ajinkya
它运行垃圾收集器,但它并没有对结果做出明确的假设(这就是“尽力而为”的部分...您永远无法确定可以释放多少内存),但是GC会被执行。我没有遇到任何不在System.gc()上执行GC的JVM。 - Neet
但是这个错误再次只涉及“JVM配置”,而不是实现。叹气他们应该最终在一个术语上达成一致,要么是“配置”,要么是“实施”。 - Neet
显示剩余4条评论

1

这个标签

System.gc() 

调用Java中的垃圾回收,建议虚拟机运行垃圾回收。虽然它不能保证实际执行垃圾回收,但这是你最好的选择。正如其他回答中提到的,自JDK 6更新7以来,jvisualvm实用程序也提供了垃圾功能。
编辑:
你的问题引起了我对该主题的兴趣,我找到了这个资源: oracle gc resource

1

你不应该依赖于 System.gc() - 如果你觉得需要强制运行GC,通常意味着你的代码/设计有问题。如果准备好被创建,GC会运行并清除未使用的对象 - 请验证你的设计并更多地考虑内存管理,同时查找对象引用中的循环。


1
应用程序的性能表现相当快,但随着时间的推移,似乎会消耗大量内存并变慢。
这些是Java内存的典型症状。很可能在您的应用程序中有一个数据结构不断增长。随着堆接近满时,JVM花费越来越多的时间运行GC,试图挽回一些空间(徒劳无功)。
强制GC不能解决此问题,因为GC无法收集数据结构。实际上,强制GC运行只会使应用程序变得更慢。
解决问题的方法是找到导致内存泄漏的原因,并进行修复。

这是几个小时内的第二个类似的编程问题.... http://stackoverflow.com/questions/14810303/java-heap-size-increasing/14810443#14810443 - Stephen C
谢谢Stephen。是的,我正在调试代码并发现了这样的数据结构。 - DotNet

1
性能的提升/下降取决于垃圾回收的频率、jvm的内存大小以及程序所需的内存量。在调用System.gc()时,垃圾回收没有确定性(仅为解释器提供提示),但至少有一定的概率。通过足够数量的调用,您可以为您的系统设置实现一些统计推导出的性能倍增器。下图显示了一个示例程序的执行消耗,并分别为每个试验分配了1GB(无gc)、1GB(gc)、3GB(gc)、3GB(无gc)堆。

首先,当程序需要3.75GB而jvm只有1GB内存时,生产者线程池完成其工作需要50秒以上,因为较少的垃圾管理导致对象创建速率较低。

第二个示例大约快了40%,因为在每个150MB对象数据的生成之间调用了System.gc()。

在第三个示例中,jvm被赋予了3GB内存空间,同时保持System.gc()开启。如预期的那样,更多的内存提供了更好的性能。

但是,在相同的3GB环境中关闭System.gc()时,速度更快!

即使我们无法强制执行,但如果我们尝试足够长的时间使用System.g(),我们仍然可以获得一些性能增益或减少。至少在我的Windows-7 64位操作系统上,使用最新的jvm。

enter image description here


0

我不建议你这样做,但是如果你想在Java代码中强制运行垃圾收集器,你可以使用所有可用的内存,这是有效的,因为垃圾收集器会在JVM抛出OutOfMemoryError之前运行...

  try {
        List<Object> tempList = new ArrayList<Object>();
        while (true) {
            tempList.add(new byte[Integer.MAX_VALUE]);
        }
    } catch (OutOfMemoryError OME) {
       // OK, Garbage Collector will have run now...
    }

它表示“当垃圾收集器无法提供更多内存时抛出”。请参阅:http://docs.oracle.com/javase/6/docs/api/java/lang/OutOfMemoryError.html - Ajinkya

0

垃圾回收器会自动运行。你不能强制垃圾回收器。


0

我的答案与其他人的不同,但会导致相同的结果。

解释:

是的,可以通过同时使用两种方法并按相同顺序来强制垃圾收集器,这两种方法是:

System.gc();

System.runFinalization();

这两个方法的调用将强制垃圾收集器执行任何不可达对象的finalise()方法并释放内存。然而,软件的性能会显著下降,这是因为垃圾运行在自己的线程中,并且对于它没有控制的方式,取决于垃圾收集器使用的算法,可能会导致不必要的过度处理。最好检查您的代码,因为它必须被打破,以便您需要使用垃圾收集器以良好的方式工作。

注意:请记住,仅当在finalize方法中不存在对象的重新分配时,此方法才有效果。如果发生这种情况,则对象将保持活动状态,并且可能会出现技术上可能的复活。


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