CMS-并发标记清理收集器
并发标记清除(CMS)收集器是为那些喜欢短垃圾收集暂停时间的应用程序设计的,并且可以在应用程序运行时与垃圾收集器共享处理器资源。一般来说,具有相对较大的长期数据集(长期生成)并运行在具有两个或更多处理器的计算机上的应用程序通常会从使用此收集器中受益。但是,任何不需要长暂停时间的应用程序都应该考虑使用这个收集器。CMS收集器是通过命令行选项-XX:+useconcmasweepgc启用的。
类似于其他可用的收集器,CMS收集器代代相传;因此,年轻一代收集和老一代收集都会发生。CMS收集器尝试通过使用单独的垃圾收集器线程来跟踪可到达的对象,该线程与应用程序线程同时执行,从而减少了主收集所导致的暂停时间。在每个主要的收集周期中,CMS collector会在收集开始时短暂暂停所有应用程序线程,并在收集过程中再次暂停。第二次停顿通常是两次停顿中较长的一次。在两次暂停期间,使用了多个线程进行收集。其余的收集工作(包括跟踪大多数实时对象和清理不可及的对象)由一个或多个与应用程序同时运行的垃圾收集器线程来完成。年轻代收集可以与正在进行的老代收集交错进行,并且以类似于并行垃圾收集器的方式执行(特别是在年轻代收集期间,应用程序线程被停止)。
并发模式失败。
CMS收集器使用一个或多个垃圾收集器线程,它们与应用程序线程同时运行,目标是在期限到期前完成收集。如上所述,在正常操作期间,CMS收集器在应用程序线程仍在运行时完成大部分跟踪和清理工作,因此应用程序线程只会看到短暂的暂停。但是,如果CMS收集器不能在老一代填满之前完成不可到达对象的收集,或者如果老一代中的可用空间块不能满足分配,则应用程序将暂停并完成收集,所有应用程序线程停止。不能并发完成收集称为并发模式失败,表示CMS收集器的参数需要调整。如果并发收集被显式垃圾收集(System.gc())或为诊断工具提供信息所需的垃圾收集中断,则将报告并发模式中断。
GC时间过长和内存不足错误。
如果垃圾收集时间过长,CMS collector会抛出OutOfMemoryError:如果垃圾收集时间超过总时间的98%,而恢复的堆少于2%,那么就会抛出OutOfMemoryError。这个函数是为了防止应用因为堆太小而长时间运行而进展甚微。如果需要,您可以通过在命令行中添加选项-XX:-UseGCOverheadLimit来禁用这个特性。
该策略与并行收集器中的策略相同,只是执行并发收集的时间不包括在98%的时间限制中。换句话说,只有在应用程序停止时进行的收集才会被计入过多的GC时间。这种收集通常是由于并发模式故障或显式收集请求(例如,对System.gc的调用)造成的。
漂浮垃圾
与Java HotSpot VM中的所有其他收集器一样,CMS收集器是一个跟踪收集器,它至少可以识别堆中所有可到达的对象。用理查德·琼斯和拉斐尔·d·林斯在他们的出版物《垃圾收集》中的话说。根据理查德·琼斯和拉斐尔·d·林斯在他们的出版物《垃圾收集:自动动态内存算法》中的说法,它是一种增量更新收集器。由于应用程序线程和垃圾收集器线程在主收集过程中同时运行,垃圾收集器线程跟踪的对象可能会在收集过程结束时变得不可访问。这种没有被回收的不可及的物体叫做漂浮垃圾。浮动垃圾的数量取决于并发收集周期的持续时间和应用程序引用更新的频率,这也称为突变。此外,由于年轻一代和老一代是独立收集的,因此各自是另一方的基本来源。作为一个粗略的指导方针,尝试将老一代的大小增加20%,以考虑到浮动垃圾。在并发收集周期结束时,堆中的浮动垃圾在下一个收集周期中被收集。
中止
在并发收集周期中,CMS collector将暂停应用程序两次。第一次暂停是为了标记直接来自根的对象(例如,来自应用程序线程堆栈和寄存器的对象引用、静态对象等)。)和堆里其他地方的对象(比如年轻人)一样活着。第一次暂停称为初始标记暂停。第二次暂停发生在并发跟踪阶段的末尾。它可以找到并发跟踪遗漏的对象,这些对象是在CMS收集器完成跟踪一个对象后被应用程序线程遗漏的。这第二次暂停称为重新标记暂停。
并发阶段
可达对象图的并发跟踪发生在初始标记暂停和重新标记暂停之间。在这个并发跟踪阶段,一个或多个并发垃圾收集器线程可能正在使用本可以提供给应用程序的处理器资源。因此,在此阶段和其他并发阶段,即使应用程序线程没有挂起,计算应用程序也可能会看到相应的应用程序吞吐量下降。在重新标记暂停之后,并行清理阶段收集被确认为不可访问的对象。一旦一个收集周期完成,CMS收集器将在消耗很少计算资源的情况下等待,直到下一个主要收集周期开始。
启动并发收集周期
在串行收集器中,只要保证年轻一代是满的,就会发生垃圾收集,当收集完成时,所有的应用程序线程都会停止。相比之下,并发收集的开始必须保证收集能够在老年期结束之前完成;否则,由于并发模式的失败,应用程序将观察到长时间的暂停。有几种方法可以启动并发收集。
根据最近的历史记录,CMS collector会对“旧代”耗尽之前的剩余时间以及并发收集周期所需的时间进行估计。使用这些动态估计,启动并发收集周期,以在旧代耗尽之前完成收集周期。为了安全起见,这些估计都是加权的,因为并发模式的失败会造成很大的损失。
如果旧代的占用率超过启动占用率(旧代的百分比),也将启动并发收集。这个启动占用率阈值的默认值大约是92%,但是这个值会随着版本的变化而变化。这个值可以通过命令行选项-xx:cmsintitingaccouncyfraction =
计划暂停
年轻一代收集的暂停和老一代收集的暂停独立发生。它们不重叠,但是它们可以快速连续地发生,因此一个集合的暂停,然后是另一个集合的暂停,将表现为单个长时间的暂停。为了避免这种情况,CMS collector会尝试在上一代暂停和下一代暂停之间安排一个重新标记的暂停,大约在中间。目前,初始标记暂停没有这样的安排,它通常比重新标记暂停短得多。
增量方式
请注意,增量模式在Java SE 8中已被弃用,在未来的主要版本中可能会被删除。
CMS收集器可以用于以增量方式完成并发阶段的模式。回想一下,在并发阶段,垃圾收集器线程使用一个或多个处理器。增量模式的目的是通过定期停止并发阶段并将处理器返回给应用程序来减少长并发阶段的影响。这种模式在这里被称为i-cms,它将收集者的并发工作分成小块时间,并安排在年轻一代之间。当CMS collector提供的需要较短暂停时间的应用程序运行在处理器数量较少的机器上时(例如,1或2),此函数非常有用。
并发收集周期通常包括以下步骤。
1-停止所有应用程序线程,确定可从根访问的对象集,然后恢复所有应用程序线程。
2-当应用程序线程执行时,一个或多个处理器被用来同时跟踪可达对象图。
3-使用处理器,追溯自上一步以来修改过的对象图部分。
4-停止所有应用程序线程,追溯自上次检查以来可能已被修改的根和对象图的部分,然后恢复所有应用程序线程。
5-同时使用一个处理器将不可到达的对象扫入空闲列表进行分配。
6-同时调整堆的大小,为下一个收集周期准备支持数据结构,使用处理器。
一般来说,CMS collector在整个并发标记阶段使用一个或多个处理器,不会主动放弃这些处理器。类似地,在整个并发清理阶段使用一个或多个处理器,它也不会被放弃。这种开销可能会对响应时间有限的应用程序造成太多干扰,否则可能会使用处理核心,尤其是在只有一个或两个处理器的系统上。增量模式解决了这个问题,它将并发阶段分解为短期活动,安排在小停顿的中间。
I-cms模式使用一个占空比来控制CMS collector在自愿放弃处理器之前允许执行的工作负载。占空比是允许CMS收集器在年轻代之间运行的时间百分比。I-cms模式可以根据应用程序的行为自动计算占空比(推荐的方法,称为自动调步),或者在命令行上将占空比设置为固定值。
* *命令行选项* *
控制i-cms模式的命令行选项。推荐选项
推荐选项
要在Java SE 8中使用i-cms,请使用以下命令行选项。
-XX:+useconcmasweepgc-XX:+CMS incrementalmode
-XX:+printgc details-XX:+printgc timestamps
前两个选项分别启用CMS collector和i-cms。后两个选项不是必需的,它们只是将关于垃圾收集的诊断信息写入标准输出,以便稍后可以看到和分析垃圾收集行为。
对于Java SE 5和更早的版本,Oracle建议将以下选项作为i-cms的初始命令行选项集。
-XX:+useconcmasweepgc-XX:+CMS incrementalmode
-xx:+printgc details-xx:+printgc timestamps。
-XX:+CMS incrementalpacing-XX:CMS incrementaldutycyclemin = 0
-XX:CMS incrementaldutycycle = 10
虽然控制i-cms自动速度的三个选项的值已经成为JavaSE6中的默认值,但是建议对JavaSE8使用相同的值。
基本故障排除
i-cms的自动调步功能利用程序运行时收集的统计数据来计算占空比,从而在堆变满之前完成并发收集。然而,过去的行为不能完美地预测未来的行为,并且估计的结果可能不总是足够准确以防止堆变满。如果有太多完全加载的器械包,请尝试下表“i-cms自动起搏功能故障排除”中的步骤,一次一个。
i-cms自动计步功能故障排除
测量结果
下图显示了CMS collector的输出,带有选项-verbose:gc和-XX:+PrintGCDetails,其他一些细节已经被删除。请注意,CMS收集器的输出与其他收集器的输出交织在一起;通常,许多二次采集会在一个同步采集周期中发生。
CMS-initial-mark表示并发收集周期的开始。
CMS-concurrent-mark表示并发标记阶段的结束。
CMS-concurrent-sweep表示并行清洗阶段的结束。
之前没有讨论过的是以CMS-concurrent-preclean为代表的预清洗阶段。预清洗是指在重新贴标阶段为CMS备注做准备的同时可以进行的工作。最后一个阶段,以CMS-concurrent-reset为代表,是为下一次并发采集做准备。
最初的标记暂停时间与整个年轻一代集合的暂停时间相比通常很短。并发阶段(同时打标、同时预清洗、同时清洗)通常比年轻一代的暂停时间长很多,如上图所示。但是,请注意,在这些并发阶段,应用程序不会暂停。重新标记暂停的长度通常相当于年轻一代习得的长度。重新标记的暂停受一些应用程序特征(例如,对象的高修改率将增加暂停)和自年轻一代的最后一次收集以来的时间(例如,年轻一代的更多对象将增加暂停)的影响。