陷阱 - 调用 System.gc() 是低效的

调用 System.gc()(几乎总是)是一个坏主意。

gc() 方法的 javadoc 指定以下内容:

“调用 gc 方法表明 Java 虚拟机花费了很多精力来回收未使用的对象,以使它们当前占用的内存可用于快速重用。当控制从方法调用返回时,Java 虚拟机已尽最大努力回收所有废弃物体的空间。“

从中可以得出以下几点:

  1. 使用建议一词而不是(说)告诉意味着 JVM 可以自由地忽略该建议。默认的 JVM 行为(最新版本)是遵循建议的,但是这可以通过在启动 JVM 时设置 -XX:+DisableExplicitGC 来覆盖。

  2. 短语从所有丢弃的对象中回收空间的最佳努力意味着调用 gc 将触发完整垃圾收集。

那么为什么称 System.gc() 是一个坏主意呢?

首先,运行完整的垃圾收集是很昂贵的。完整的 GC 涉及访问和标记仍可到达的每个对象; 即每个不是垃圾的物体。如果在没有太多垃圾需要收集的情况下触发此操作,那么 GC 会做很多工作而收益相对较小。

其次,完整的垃圾收集可能会干扰未收集的对象的位置属性。大致在同一时间由同一线程分配的对象往往在内存中靠近分配。这很好。同时分配的对象可能是相关的; 即彼此参考。如果你的应用程序使用这些引用,那么由于各种内存和页面缓存效应,内存访问的可能性会更快。不幸的是,完整的垃圾收集往往会移动对象,以便曾经关闭的对象现在更加分开。

第三,运行完整的垃圾收集可能会使你的应用程序暂停,直到收集完成。发生这种情况时,你的应用程序将无响应。

实际上,最好的策略是让 JVM 决定何时运行 GC,以及运行什么样的集合。如果不干扰,JVM 将选择优化吞吐量或最小化 GC 暂停时间的时间和集合类型。

一开始我们说“……(几乎总是)一个坏主意……”。事实上,有几种情况可能是个好主意:

  1. 如果你正在为某些对垃圾收集敏感的代码实现单元测试(例如涉及终结器或弱/软/幻像引用的内容),则可能需要调用 System.gc()

  2. 在一些交互式应用程序中,可能存在特定时间点,用户不关心是否存在垃圾收集暂停。一个例子是游戏中存在自然暂停的游戏; 例如,当加载新级别时。