23

AFAIK 当 GC 正在做它的事情时,VM 会阻塞所有正在运行的线程——或者至少在它压缩堆时。在 CLR 和 JVM (2010 年 1 月的生产版本)的现代实现中是这种情况吗?请不要提供有关 GC 的基本链接,因为我了解基本的工作原理。

我假设全局锁定是这种情况,因为当压缩发生时,引用可能在移动期间无效,并且似乎最简单的方法只是锁定整个堆(即通过阻塞所有线程间接地)。我可以想象更强大的机制,但 KISS 经常占上风。

如果我不正确,我的问题将通过对用于最小化阻塞的策略的简单解释来回答。如果我的假设是正确的,请对以下两个问题提供一些见解:

  1. 如果这确实是这种行为,那么像 JBOSS 和 Glassfish 这样的重量级企业引擎如何保持始终如一的高 TPS 率?我在 JBOSS 上进行了一些谷歌搜索,我期待在 APACHE 上找到一些适合 Web 处理的内存分配器。

  2. 面对 NUMA 式的架构(可能在不久的将来),这听起来像是一场灾难,除非进程是受线程和内存分配限制的 CPU。

4

6 回答 6

16

答案是这取决于所使用的垃圾收集算法。在某些情况下,您在 GC 期间停止所有线程是正确的。在其他情况下,您在正常线程运行时进行垃圾收集是不正确的。要了解 GC 是如何实现这一点的,您需要详细了解垃圾收集器的理论和术语,并结合对特定收集器的了解。它根本不适合简单的解释。

哦,是的,值得指出的是,许多现代收集器本身并没有压实阶段。相反,它们通过将活动对象复制到新的“空间”并在完成后将旧的“空间”归零来工作。

如果我不正确,我的问题将通过对用于最小化阻塞的策略的简单解释来回答。

如果你真的想了解垃圾收集器的工作原理,我建议:

...并且要注意,要找到生产垃圾收集器内部的准确、详细、公开的描述并不容易。(虽然在 Hotspot GC 的情况下,您可以查看源代码......)

编辑:回应OP的评论......

“这似乎和我想的一样——‘停止世界’这部分是无法绕过的。”

这取决于。在Java 6 Concurrent Collector的情况下,在标记根(包括堆栈)期间有两次暂停,然后并行进行其他对象的标记/复制。对于其他类型的并发收集器,在收集器运行时使用读或写屏障来捕获收集器和应用程序线程否则会相互干扰的情况。我现在没有 [Jones] 的副本,但我还记得有可能使“停止世界”间隔可以忽略不计……代价是更昂贵的指针操作和/或不收集所有垃圾。

于 2010-01-18T11:59:33.480 回答
2

您是正确的,垃圾收集器将不得不暂停所有应用程序线程。Sun JVM 可以通过使用并发收集器来减少这种暂停时间,该收集器在不停止应用程序的情况下执行一些工作,但它必须暂停应用程序线程。

见这里http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html#par_gc和这里http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html#cms有关 sun JVM 如何管理最新 JVM 中的垃圾收集的详细信息。

对于 Web 应用程序,我认为这不是问题。由于用户请求应在小于 1 秒的时间内完成,因此分配给请求服务的任何临时对象不应退出年轻代(只要其大小合适),它们会被非常有效地清理。其他生命周期较长的数据(例如用户会话)会保留更长时间,并可能影响花费在主要 GC 事件上的时间。

在高 TPS 应用程序上,一个常见的策略是使用会话关联和负载平衡在相同或单独的硬件上运行应用程序服务器的多个实例。通过这样做,每个 JVM 的单个堆大小保持较小,从而减少了执行主要收集时 GC 的暂停时间。一般来说,数据库成为瓶颈,而不是应用程序或 JVM。

在 J2EE 中,您可能会发现与 Web 特定内存分配器概念最接近的是由框架和应用程序服务器执行的对象/实例池。例如,在 JBOSS 中有 EJB 池和数据库连接池。然而,这些对象通常是池化的,因为它们的创建成本很高,而不是垃圾收集开销。

于 2010-01-18T12:40:39.860 回答
1

我相信 IBM 已经进行了一些研究以提高多核系统中的 GC 性能,其中包括减少或消除“一切都停止”问题的工作。

例如,请参阅: A Parallel, Incremental and Concurrent GC for Servers(pdf)

或谷歌诸如“并发垃圾收集ibm”之类的东西

于 2010-01-18T13:02:26.250 回答
1

AFAIK 当 GC 正在做它的事情时,VM 会阻塞所有正在运行的线程——或者至少在它压缩堆时。在 CLR 和 JVM(2010 年 1 月的生产版本)的现代实现中是这种情况吗?

Sun 的 Hotspot JVM 和 Microsoft 的 CLR 都具有并发 GC,它们仅在短阶段(以获取全局根的自洽快照,所有实时数据都可以从中访问)而不是整个收集周期。我不确定他们的压缩实现,但这是非常罕见的情况。

如果这确实是这种行为,那么 JBOSS 和 Glassfish 等重量级企业引擎如何保持始终如一的高 TPS 率?

这些引擎的延迟比停止世界所需的时间长几个数量级。此外,延迟被引用为例如第 95 个百分位数,这意味着延迟将仅在 95% 的时间内低于引用的时间跨度。所以压缩不太可能影响引用的延迟。

于 2010-08-17T13:41:11.630 回答
0

Java 有许多可用的 GC 算法,但并非所有这些算法都会阻塞所有正在运行的线程。例如,您可以使用与应用程序同时运行的 -XX:+UseConcMarkSweepGC(用于收集终身代)。

于 2010-01-18T11:49:11.960 回答
0

当前最先进的 Java 垃圾收集仍然涉及偶尔的“停止世界”暂停。Java 6u14 上引入的 G1 GC 的大部分工作都是并发的,但是,当内存非常低,并且需要压缩堆时,它必须确保没有人弄乱它下面的堆。这要求不允许进行任何其他操作。要了解有关 G1 GC 的更多信息,请查看Sun 的演示文稿

于 2010-01-18T12:48:47.310 回答