luaC_barrierback和luaC_barrierf 区别
在GC过程中对新建的对象引用关系进行处理。
例如:o 引用 v
luaC_barrierf : 扫描阶段将v重新标记(V加入到当前GC),否则将o标记为white
luaC_barrierback:将o加入到grayagain中。grayagain在原子操作中进行处理
luaC_barrierback用于监控table的key和value 变化。因为table是易变的,如果使用luaC_barrierf ,那么每次改变key或value都重新扫描新对象,导致标记时间的增长。
所以使用luaC_barrierback当发生改变时,见其加入grayagain中,这就将table分为了两种,一种是未改变,一种是改变的,再次发生改变时也不会重复加入到grayagain中。
等待amotic阶段重新扫描真个table表
String:
字符串存储在开散列的hash表中,GCSsweepstring 每次清理散列表中的一列。由于清理也是分步进行的,当在字符串清理阶段发生resize散列表,有可能未清理部分的字符串会放置到以清理部分,
导致字符串没办法在当前GC重置为white。
table:
weak table 只有在所有对象mart完才能确定哪些节点需要释放,所以weak table 一直为gray(引用对象未遍历),所以对weak table的修改不会触发 barrier(只有black的节点才会触发barrier)。
所以在mart 阶段结束后,table中可能还会存在一些强引用。所以在atomic 阶段对weak table 进行remart,所有的weak table在 mart阶段都被放入到g->weak链表中。
线程:
线程引用的是线程堆栈,在运行时整个堆栈都是一直变化的,如果加入luaC_barrier监控变化,会导致运行效率降低,所以Lua默认线程就是一直在变的,在mart是不会标记为black,而是像处理table一样在amotic阶段重新扫描。
upvalue:
新建的upvalue(打开状态)存储在g->uvhead和L->openupval中,不连接在alloc链表上。当close upvalue时将upvalue放回到alloc中并从openupvalue和uvhead中移除。所以处理GC处理upvalue时,如果是close upvalue 则像普通对象一样处理。open upvalue则需要特殊处理。openupvalue 只有可能被线程和lua closure引用,而只被线程引用而没被lua closure引用的upvalue 是无意义的,应该被清除掉。所以线程对upvalue是弱引用。当mart阶段只会将open upvalue 从white 变为 gray,用于标记是否被lua closure引用。在清理阶段,线程对象遍历自己的upvalue ,清理掉没有被引用到的upvalue。当线程退出时也会清理open upvalue。open upvalue 由于引用栈上的值,变化频繁,所以也需要在atomic阶段重新remart。
userdata处理:
由于userdata由_gc元方法存在,清理之前应该调用_gc元方法。所以需要遍历所有的userdata,Lua将userdata存储在alloc链表中,但是区别于其他对象头插入到alloc中,userdata插入到线程对象之后,其他对象-->线程-->userdata。在atomic阶段 通过luaC_separateudata 查找所有已经"dead",并将带_gc 方法的userdata,将其从alloc链表中移除,加入到tmudata双向链表中,并标记FINALIZEDBIT,防止_gc元方法重复调用。在GCSfinalize阶段遍历tmudata,调用_gc元方法并将其从tmudata中移除,重新加入到alloc链表中。dead的userdata并不在本次gc中回收,而是先做标记然后调用_gc元方法,然后在下一次的GC中回收。
base type metatable:
基本类型的元表是共享的,而不是像table 等 是每个对象独享的。所以 base type metatable 存储在 g->mt中(也在alloc中)。当设置元表时,table 等会触发barrier,但是base type metatable 由于存储结构关系,barrier无法处理,所以并不会触发barrier。所以在atomic阶段会重新remart base type metatable(mart 过程中引用关系的改变)。