手动 GC 调用的用例?

A use case for a manual GC invocation?

我读过 why is it bad practice to call System.gc(), and many others, e.g. this one describing a really disastrous misuse of System.gc(). However, there are cases when the GC takes too long and avoiding long pauses, e.g., by avoiding garbage 并不是很简单并且使代码更难维护。

恕我直言,在以下常见情况下手动调用 GC 没问题:

该算法似乎很简单:定期 select 一个服务器,不再向其发送请求,让它完成其 运行 请求,让它进行 GC,然后重新激活服务器。

不知道是不是漏了什么?1,2

有哪些替代方案?

  1. 长 运行 请求可能是个问题,但我们假设有 none。或者简单地将等待时间限制在与 GC 花费的时间相当的时间段内。使一个缓慢的请求变得更慢听起来也不错。

  2. -XX:+DisableExplicitGC这样的选项可能会使算法无用,但不要使用它(我的用例包括我负责的专用服务器)。

对于低延迟交易系统,我以非典型方式使用 GC。

您想避免在交易日内收集任何东西,即使是少量收集。一种方法是每秒创建少于 300 KB 的垃圾。这大约是每小时 1 GB,或每天最多 24 GB。当您使用 24 GB Eden space 时,这意味着没有 minor/major GC。但是,为了确保 GC 在计划好的和可接受的时间发生,System.gc() 会在每天早上 5 点被调用,这样您就可以在第二天拥有一个干净的 Eden space。

有时候,您制造的垃圾比预期的要多,例如无法重新连接到数据源,您可能会得到少量次要集合。然而,这只有在出现问题时才会发生。

更多详情http://vanillajava.blogspot.co.uk/2011/06/how-to-avoid-garbage-collection.html

by avoiding garbage is not exactly trivial and makes the code harder to maintain.

完全避免垃圾几乎是不可能的。然而 300 KB/s 对于 JVM 来说并不难。 (如今,一台具有 24 GB Eden spaces 的机器上可以有多个 JVM)

请注意,如果您可以将垃圾保持在 50 KB/s 以下,那么您可以 运行 整个星期都不需要 GC。

Periodically select a server, let no more requests be send to it, let it finished its running requests, let it do its GC, and re-activate the server.

您可以将 GC 视为未能满足您的 SLA 条件。在这种情况下,当您确定这将要从您的集群中发生时,您可以删除服务器,对其进行 Full GC 并将其 return 到集群。

However, there are cases when the GC takes too long and avoiding long pauses

您必须区分由年轻代、mixed/concurrent 阶段和完整 GC 引起的暂停。

在大多数情况下,您希望避免完整的 GC,而其他的则可以接受,这通常可以通过一些 GC 调整和优化代码来避免大量分配突发。

原则上 G1 应该能够在 young/mixed 周期内永远 运行 并且完整的 GC 可以被视为软故障。通过仔细调整,CMS 至少可以这样做很多天,但最终可能会出现碎片并需要完整的 GC 进行压缩。

如果即使是年轻的 GC 暂停也不可接受,或者垃圾堆积得太快以至于并发阶段无法处理可接受的暂停时间,那么您概述的策略可能是一个可行的解决方法。

请注意,还有其他手动触发 GC 的用例,例如 GC 管理的本机资源,例如直接字节缓冲区,尽管这些通常很麻烦。

另请注意,并非所有 System.gc() 调用都是平等创建的,也有 ExplicitGCInvokesConcurrent 选项。