垃圾收集和线程

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

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

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

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

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

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

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

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

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

  • Richard Jones 撰写的 “ 垃圾收集:自动动态内存管理算法” 。
  • 理查德·琼斯,安东尼·霍斯金和艾略特·莫斯的“垃圾收集手册:自动memory management的艺术”

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

编辑:回应OP的评论……

“似乎就像我想的那样 – 没有绕过”阻止世界“的一部分。”

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

你是正确的,垃圾收集器将必须暂停所有应用程序线程。 使用并发收集器可以通过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应用程序,我认为这不是问题。 由于用户请求应在<1s的少量时间内完成,因此分配给服务请求的任何临时对象都不应退出年轻代(如果大小合适),以便非常有效地清理它们。 具有较长生命周期的其他数据(例如用户会话)将更长时间地停留并且可能影响花费在主要GC事件上的时间。

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

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

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

例如,请参阅: 服务器的并行,增量和并发GC(pdf)

或谷歌之类的“并发垃圾收集ibm”

AFAIK当GC正在执行其操作时,VM会阻止所有正在运行的线程 – 或者至少在压缩堆时。 这是CLR和JVM(生产版本截至2010年1月)的现代实现中的情况吗?

Sun的Hotspot JVM和Microsoft的CLR都有并发的GC,它们只在短期内停止世界(获得所有实时数据可以访问的全局根的自我一致快照),而不是整个收集周期。 我不确定他们的压实实现,但这种情况非常罕见。

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

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

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

Java的当前最先进的垃圾收集仍然偶尔会“停止世界”暂停。 在Java 6u14上引入的G1 GC可以同时完成大部分工作,但是,当内存非常低时,它需要压缩堆,然后它必须确保没有人混淆堆下面的堆。 这要求不允许任何其他事情继续进行。 要了解有关G1 GC的更多信息,请查看Sun的演示文稿 。