在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
luaC_fullgc :进行一次完整的gc
总体上分为两部分
(1)完成上次未完成GC
(2)重新开始一次完整GC。
因为在(2)中会进行完整GC,在(1)部中并不需要真正的完成GC(GC的标记消耗是很大的)只需要将节点标记为white交由(2)重新处理即可。
在(1)中跳过了标记阶段,这里没有翻转白色标记,然后交给sweep 将节点重置为white
PS:luaC_fullgc 是原子性操作
GCthreshold :
totalbytes:通过alloc管理的内存数量
estimate:是一个内存预估值 estimate = totalbytes - udataByte PS:estimate <= totalbytes
setpause和collect 对垃圾回收的影响????
标记:
对象之间的引用关系
GC的标记过程不是递归的,将这个过程拆分成几个时间片执行。
标记操作是调用reallymarkobject ,将节点转换 white ----> gray 。
因为 string 没有引用对象,但是却没有标记为black,这是一个小优化,只要不是white就不会清理,所以没必要再讲string 的颜色从gray ---> black。
userdatahe upvalue结构相对简单,所以在这里标记所引用的对象,同时将其转换为black(这里会再一次调用reallymarkobject ,但是不会产生一个深度递归,因为upvalue 不会引用 另一个upvalue,所以标记操作的时间复杂度是O(1))。 打开的upvalue不会转换成black,打开的upvalue 引用了堆栈上的对象,而堆栈上的对象是易变的。标记是分段执行的,如果此时将其标记为黑色,那么栈上的值发生改变会引发问题。(???)
其他的结构这是把节点加入到gray list中等待后续的处理。
标记时间的复杂度O(1),保证了比较耗时的标记阶段可以分派到多个时间片中执行,而不需要长时间的等待整个标记操作的完成。
扫描阶段:
将节点转换 gray---->black ,标记引用对象。
扫描table:weak table 会在扫描的如atomic中进行清理操作,如果扫描是发现table,那么将其加入到g->weak list中,等待清理。weak table重新转换成 gray。
扫描thread:扫描节点,从gray list移除节点,加入到grayagain list,等待再次扫描。 这是因为 thread 关联的对象其实是运行堆栈,堆栈是随着运行过程不断变化的。没有必要为最频繁的 stack 修改做 barrier ,那样对性能影响就很大了。最简单的办法就是把 thread 推迟到 atomic 阶段重扫描一次。这里第一次的扫描,是为了减少在atomic 扫描的时间(atomic 的扫描时间越少越好)。
扫描proto: 常量 、upvaluename 、子proto 、local value name。在proto的创建阶段会有大量的对象产生,很容易发生GC。
当GC阶段发生对象之间的引用变化那么就会执行 write barrier(保护)
luaC_barrierback和luaC_barrierf 区别???
o 引用 v
luaC_barrierf : 扫描阶段将v重新标记(V加入到当前GC),否则将o标记为white(white并不会导致被清除,只是留到下一次gc处理)。
luaC_barrierback:将o加入到grayagain中。(这个函数只在table引用的对象发生变化调用)
table的修改很频繁,如果是用luaC_barrierf 实现,频繁改变table会导致不断有对象被加入到graylist中,可能导致扫描永远完不成。
注意,对弱表的修改是不会触发 luaC_barrierback 的。因为弱表不会是黑色。弱表也不能触发它。在扫描过程中,对弱表的修改又不会触发 barrier 。这会导致最终扫描完成后,某些弱表里可能还藏有一些新建立的强应用关系。所以atomic 节点需要再次 mark。
在lua_setmetatable中设置全局的mt,并不会触发luaC_barrierback 。所以在atomic 节点需要再次 mark g->mt
清理阶段:
字符串清理:字符串存储在开散列的hash表中,GCSsweepstring 每次清理散列表中的一列。由于清理也是分步进行的,当在字符串清理阶段发生resize散列表,有可能未清理部分的字符串会放置到以清理部分,导致字符串没办法在当前GC重置为white。待清理阶段字符串复活直接重置为white。
垃圾对象清理:存活重置为white 死亡释放空间。
UserData清理:通过GCTM 调用一个userdata 的 gc 元方法,此时调整GCthreshold 的值避免发生自动GC。
PS:string 和 upvalue 是全局唯一的,为了查找效率考虑,并没有存放在rootgc ,而是进行单独的存放。所以GC部分要对其特殊处理。
userdata处理:
userdata可能有自带的gc方法,当回收空间之前,需要调用自带的gc方法。所以userdata需要单独的处理。rootgc是头插入链表, 线程是其第一个对象, userdata被放置到线程对象之后,rootgc (其他对象-->线程-->userdata)。这样就可以高效率的遍历所有userdata进行处理。在luaC_separateudata 中查找出所有有gc方法的userdata,保存到g->tmudata。然后在atomic阶段将其标记为不可清除(保证在调用自带gc方法前,对象内存不会被Lua GC回收掉)。
为什么白色标记要用两位???
当新建一个对象时,可能进程开始回收但未完成。这时这个对象是要等下一次回收的,如果标记为white 那么可能这次就被回收掉,如果标记为black,可能在本次回收之后不会被重置为white(回收完所有对象都要被重置为white,并且每遍历一个就重置一个)。所以这里引入两种white。
进入清理阶段之后, 将对象变为白色不会影响当前对象是否死亡的判断.
自动GC问题????
当调用栈很深的情况下,可能产生大量临时对象,这是很容易发生GC,而这时候发生GC导致很多不必要的临时变量被mark。降低了GC效率。
|
请发表评论