我分别使用 Sun 1.6 JVM 的 64 位版本(显然)在 Linux 和 Solaris 下的两个不同应用程序上使用了超过 60 GB 的堆大小。
我从来没有遇到过基于 Linux 的应用程序的垃圾收集问题,除非在堆大小限制附近推高。为了避免该场景固有的抖动问题(花费太多时间进行垃圾收集),我简单地优化了整个程序的内存使用量,使峰值使用量比 64 GB 堆大小限制低 5-10%。
然而,在 Solaris 下运行不同的应用程序时,我遇到了严重的垃圾收集问题,因此需要进行大量调整。这主要包括三个步骤:
通过 -XX:+UseParallelGC -XX:+UseParallelOldGC JVM 选项启用/强制使用并行垃圾收集器,以及通过 -XX:ParallelGCThreads 选项控制使用的 GC 线程数。有关详细信息,请参阅“ Java SE 6 HotSpot 虚拟机垃圾收集调整”。
在不再需要局部变量后,将它们广泛且看似荒谬的设置为“null”。其中大多数是超出范围后应该有资格进行垃圾收集的变量,并且它们不是内存泄漏情况,因为没有复制引用。然而,出于某种原因,这种用于帮助垃圾收集的“手持”策略对于所讨论的 Solaris 平台下的该应用程序来说是莫名其妙的必要性。
在大量临时对象分配之后,在关键代码部分中选择性地使用 System.gc() 方法调用。我知道反对使用这些调用的标准警告,以及它们通常应该是不必要的论点,但我发现它们对于在运行这个内存密集型应用程序时驯服垃圾收集至关重要。
上述三个步骤使得在大约 60 GB 的堆使用量下保持该应用程序的可控性和高效运行成为可能,而不是失控到 128 GB 的堆大小限制。尤其是并行垃圾收集器非常有用,因为当有很多对象时,主要垃圾收集周期是昂贵的,即主要垃圾收集所需的时间是堆中对象数量的函数。
我无法评论这种规模的其他特定于平台的问题,我也没有使用过非 Sun (Oracle) JVM。