lua5.4 分代gc

lua gc的发展史:
  5.0:双色标记法 非黑即白,只能原子进行,不然会出bug,缺点是会执行gc的时候进入 stop the world。
  5.1:四色标记法 中间增加了灰色,白色分为两种颜色(当前白与非当前白),解决了必须原子操作的问题,在普通的扫描传播颜色中是步进式的,解决了整个gc中 stop the world 的问题,但是在对grayagain与弱表等的操作过程中,仍然需要原子操作进入 stop the world。
  5.2: 实现了分代 gc,实际效果不理想,这个版本是只需要活过一轮gc就变成老对象,不进行回收,但是很多情况下程序会有大量的局部变量在使用。
  5.4:分代gc 在四色标记法基础上优化,根据计算机的实现原理(二八法则,局部性原理)以及使用经验总结得出当一个对象多次被使用到的时候,大概率是需要存活很长一段时间的,分代gc在这一部分进行了优化,即在适当情况下不对这些对象扫描以及回收。

1 分代模式什么时候会进行gc模式的切换?
  1. fullgen:当一次genstep的时候,新增的对象数量是上一次的一倍(取决于配置)时进行一次fullgen。
  2. 在1情况下,当fullgen执行回收掉了增长量的一半以下时(即bad collection),下一次genstep会进行stepgenfull,stepgenfull首先会进入增量gc执行一次简易的扫描阶段(restartcollection),然后进行普通gray的传播以及atomic操作(即只扫描,并不进行回收操作),执行结束后,统计剩下的对象数量,如果此次剩下的增量小于上次存活的对象数量的 1/8 倍,则再次切换回分代gc。
2 在切换回增量模式后,什么时候会切换回分代模式?
  1 fullgen后一定会切回分代模式。
  2 在一次bad collection后执行的 stepgenfull 中,如果此次时间段中新增的对象数量少于原存活下来的对象的 1/8 时,会切换回分代模式。
3 fullgen里做了什么?
  fullgen中,先会对gc相关变量进行初始化(在enterinc中操作)并切换成增量模式,在增量模式里只做了当前可达对象的传播,不会在增量模式中进行回收(entergen)。回收是在原子切换到分代模式(atomic2gen)中执行的(sweep2old)。
4 stepgenfull里做了什么?
  当上一次分代gc是一次bad collection时会进行一次 stepgenfull,此函数先会切换到增量模式进行一次完整的传播,先不进行回收,如果此次新增的对象数量少于上一次存活下来的数量的1/8则会切换至分代模式,否则继续执行增量gc到下一次开始。
5 atomic2gen --- 原子性切换到分代模式
  1 entergen 2 stepgenfull 里会调用到
  函数里会对可达对象进行标记为old,对grayagain进行收尾操作。

6 youngcollection 一次分代gc。
  将 OLD1的对象全部标记位OLD,将g->finobj里的对象全部标记为OLD,将g->finobjrold全部标记为OLD。
对需要原子操作的对象进行处理,将全部可达的G_NEW对象标记位G_SURVIVAL,并链接到pfirstold1中,将G_SURVIVAL对象标记为G_OLD1,链接到g->firstold1,将G_NEW的finobj标记为G_SURVIVAL,将G_SURVIVAL的finobj标记为G_OLD1,对tobefnz的对象进行处理。最后对grayagain,弱表 的对象进行状态推进以及回收。
完成一次youngcollection

7 各状态的理解
  old的定义是状态大于 G_SURVIVAL

  G_NEW:本次cycle创建的新对象(没有引用任何old对象)
  G_SURVIVAL:上一轮cycle创建的对象 -- 只活过一轮,下一次如果是白色的话,仍然会被回收。
  G_OLD0:表示本次cycle创建的新对象,但是引用了old对象,需要barrier操作。为什么不直接设置成old对象?同G_SURVIVAL,是实现的问题。
  G_NEW,G_SURVIVAL,G_OLD0表示都不是老对象

  G_OLD1:表示作为老对象第一次存活了整个gc过程 -- 为什么需要G_OLD1(从G_SURVIVAL或者G_OLD0变成G_OLD1,而不是直接从G_SURVIVAL或者G_OLD0变成G_OLD)?因为如果节点A现在是在G_SURVIVAL或者G_OLD0,在同一个cycle中转成old对象前,有一个子节点B引用了A节点(例:A[B]=C),这时候因为A节点仍然不是old,所以不会触发到barrier与barrier_back操作,所以节点B仍然是G_NEW,到最后,A节点直接转变成G_OLD的话,B节点转变成G_SURVIVAL,因为在youngcollection中不会对g->reallyold链表进行markold操作,所以在下一次gc中,如果B节点不可达(即是白色)的话,那么在这次gc中会被释放。虽然可以改成old也传播,但是这样的话就破坏了规则(会对所有的G_OLD都遍历,这样就达不到缩减成本的目的了)。

  G_OLD:表示真正的old对象,不会被回收 -- 作为老对象需要2次gc过程,作为新创建的对象需要经过3次gc才会到达此阶段。

  G_TOUCHED1:标记位G_OLD的对象在这次gc barrier_back的状态 -- 新touch的对象,需要进入到grayagain中

  G_TOUCHED2:标记为G_OLD的对象在上一次gc barrier_back的状态前进到touched2 -- 分代gc结束时,从G_TOUCHED1转成G_TOUCHED2,并设置为黑色,仍然存在于grayagain中,以便下一轮再次有新对象使该对象 barrier_back 时,只需要修改为灰色(使用计算机的局部性原理,这次用到的东西,下一次可能还会触发,避免下次触发时进行链表的操作)。如果下一次没有触发为touched1则变成G_OLD。在correctgraylist中会对touched2跟old的对象从grayagain删除。

posted @   小乐虎  阅读(960)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示