关于Golang GC的一些误解,真的比Java算法更领先吗?
这是理想的情况,然而现实却远没有如此简单。如果在垃圾收集过程中,P1在堆内存达到极限之前无法完成标记工作(因为应用程序可能在大量分配内存),该怎么办?如果3个Goroutines中只有一个大量分配内存导致P1无法完成标记工作,在这种情况下,分配新内存的速度会变慢,特别是始作俑者的那个Go routine分配内存的时候。 如果垃圾收集器确定需要减慢内存分配,原本运行应用程序Goroutines会协助标记工作。应用程序Goroutine成为Mark Assist(协助标记)中的时间长度与它申请的堆内存成正比。Mark Assist有助于更快地完成垃圾收集。 上图显示了在P3上运行的应用程序Goroutine现在正在执行Mark Assist并进行收集工作。 垃圾收集器的一个设计目标是减少对Mark Assists的需求。如果任何本次垃圾回收最终需要大量的Mark Assist才能完成工作,则垃圾收集器会提前开始下一个垃圾收集周期。这样做可以减少下一次垃圾收集所需的Mark Assist。 Mark终止 一旦并发标记阶段完成,下一个阶段就是标记终止。最终关闭写屏障,执行各种清理任务,并计算下一个垃圾回收周期的目标。一直处于循环中的goroutine也可能导致stw延长(类似mark setup的情况)。 上图显示了在标记终止阶段完成时如何停止所有goroutine。这一动作平均在60到90微秒之间完成。这个阶段可以在没有STW的情况下完成,但是使用STW的代码更简单。 一旦收集完成,应用程序Goroutines就可以再次使用所有P,应用程序将恢复到油门全开的状态。 上图显示了垃圾收集完成后,所有P现在都可以用于应用程序。 并发清理 标记完成后,下一阶段执行并发清理。清理阶段用于回收标记阶段中标记出来的可回收的内存。当应用程序goroutine尝试在堆内存中分配新内存时,会触发该操作。清理导致的延迟和吞吐量降低被分散到每次内存分配时。 下面是我的机器上的一个trace示例,其中有12个硬件线程可用于执行goroutine。 (编辑:西安站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |