为什么调用System.gc()是不好的做法?

在回答了关于如何使用System.gc()清除Java中强制释放对象的问题(这个人正在清除1.5GB HashMap System.gc() ,我被告知手动调用System.gc()是一种不好的做法,但是这些注释并不是完全有说服力。 另外,似乎没有人敢鼓掌,也没有倒下我的答案。

当时有人告诉我这是一种不好的做法,但后来我被告知垃圾收集器的运行不再系统地阻止世界,它也可以被JVM有效地用作提示,所以我有点在损失。

我明白,当需要回收内存时,JVM通常比你更清楚。 我也明白,担心几千字节的数据是愚蠢的。 我也明白,即使是兆字节的数据也不是几年前的情况。 但是,仍然是1.5千兆字节? 而且你知道有1.5 GB的数据存储在内存中; 它不像是在黑暗中拍摄的照片。 System.gc()系统性不好,还是有一点可以让它变好?

所以问题实际上是双重的:

  • 为什么调用System.gc()是或不是不好的做法? 它在某些实现中仅仅是JVM的一个暗示,还是总是一个完整的收集周期? 真的有垃圾收集器的实现可以在不停止世界的情况下完成他们的工作吗? 请澄清一下人们在评论中对我的回答所作的各种断言。
  • 门槛在哪里? 调用System.gc()不是一个好主意,还是有时它可以接受? 如果是这样,那是什么时候?

  • 每个人总是说避免System.gc()是,它是一个很好的指示代码基本破碎的指标。 任何依赖于正确性的代码肯定会破坏; 任何依赖它来表现的东西都很可能被打破。

    你不知道你正在运行什么类型的垃圾回收器。 肯定有一些不会像你声称的那样“阻止世界”,但是有些JVM并不那么聪明或者出于各种原因(可能它们在用手机?)不这样做。 你不知道它会做什么。

    此外,它不保证做任何事情。 JVM可能完全忽略你的请求。

    “你不知道它会做什么”,“你不知道它是否会有所帮助”以及“你不需要再调用它”的组合是人们如此强烈地表达你不应该这样称呼它。 我认为这是一个“如果你需要问你是否应该使用这个,你不应该”


    编辑来解决其他线程的一些问题:

    在阅读您关联的主题后,我想指出更多的内容。 首先,有人建议调用gc()可能会将内存返回给系统。 这当然不一定是真实的 - Java堆本身的增长独立于Java分配。

    如同在,JVM将保存内存(几十兆字节)并根据需要增加堆。 即使释放Java对象,它也不一定会将该内存返回给系统; 它可以完全自由地保留分配的内存以用于将来的Java分配。

    为了表明System.gc()可能不执行任何操作,请查看:

    http://bugs.sun.com/view_bug.do?bug_id=6668279

    特别是有一个-XX:DisableExplicitGC VM选项。


    已经解释过,调用system.gc()可能什么也不做,任何“需要”垃圾收集器运行的代码都会被破坏。

    然而,调用System.gc()是不好的做法的实际原因是效率低下。 而在最坏的情况下,这是非常低效的 ! 让我解释。

    典型的GC算法通过遍历堆中的所有非垃圾对象来识别垃圾,并推断任何未访问的对象都必须是垃圾。 由此,我们可以对垃圾收集的总体工作进行建模,其中一部分与实时数据量成比例,另一部分与垃圾量成正比; 即work = (live * W1 + garbage * W2)

    现在假设您在单线程应用程序中执行以下操作。

    System.gc(); System.gc();
    

    第一个电话会(我们预测)做(live * W1 + garbage * W2)工作,摆脱杰出的垃圾。

    第二个电话会做(live* W1 + 0 * W2)工作,不收回任何东西。 换句话说,我们完成了(live * W1)工作,并没有取得任何成果。

    我们可以将收集器的效率建模为收集垃圾单位所需的工作量; 即efficiency = (live * W1 + garbage * W2) / garbage 。 因此,为了使GC尽可能高效,我们需要在运行GC时最大化garbage的价值; 即等到堆满了。 (并且尽可能地使堆尽可能大,但这是一个单独的主题。)

    如果应用程序不会干扰(通过调用System.gc() ),则GC将在运行之前等待堆满,从而有效收集garbage1。 但是,如果应用程序强制GC运行,那么堆的可能性将不会满,结果将导致垃圾收集效率低下。 而且应用程序越频繁地使用GC,GC的效率越低。

    注意:上面的解释掩盖了这样的事实:典型的现代GC将堆分成“空间”,GC可能动态扩展堆,应用程序的非垃圾对象的工作集可能会有所不同,等等。 即便如此,相同的基本原则适用于所有真正的垃圾收集者2。 强制GC运行效率低下。


    1 - 这是“吞吐量”收集器的工作原理。 并发收集器如CMS和G1使用不同的标准来决定何时启动垃圾收集器。

    2 - 我也排除了仅使用引用计数的内存管理器,但目前的Java实现没有使用这种方法......这是很好的理由。


    很多人似乎都在告诉你不要这样做。 我不同意。 如果在像加载关卡这样的大型加载过程之后,您认为:

  • 你有很多无法访问的对象,可能没有被gc化。 和
  • 你认为用户在这一点上可以承受一个小的放缓
  • 调用System.gc()并没有什么坏处。 我把它看作是c / c ++ inline关键字。 这只是gc的一个暗示,你,开发人员已经决定时间/性能不像通常那么重要,并且其中一些可以用于回收内存。

    建议不要依靠它做任何事情都是正确的。 不要依赖它的工作,但提供现在是可以接受的时间来收集的提示是非常好的。 我宁愿浪费时间在代码中无关紧要的地方(加载屏幕),而不是在用户与程序进行积极互动时(例如在游戏级别中)。

    有一次,当我将迫使集合:试图找出是当一个特定的对象泄露(无论是本机代码或大型,复杂的回调互动哦,说了这么多为在Matlab的目光任何UI组件。) 这不应该被用来在生产代码中。

    链接地址: http://www.djcxy.com/p/54531.html

    上一篇: Why is it bad practice to call System.gc()?

    下一篇: How do I convince my colleagues not to implement IDisposable on everything?