为什么我们需要调用Lua的collectgarbage()函数两次?

我在几个地方遇到过人们调用collectgarbage()两次才能完成所有未使用的对象终结的情况。

为什么会这样?一个调用不够吗?为什么不是三次?

当我尝试以下代码(在Lua 5.2上)时,通过只调用一次“collectgarbage”,就会终止该对象(即其__gc被调用):

do
  local x = setmetatable({},{
    __gc = function() print("works") end
  })
end
collectgarbage()
os.exit()

这是否意味着一次调用就足够了?

点赞
用户1009479
用户1009479

这个问题在《Lua编程》第三版的第17.6节“终结器”中有详细解释。简而言之,这是因为对象被复活了。

终结器是与对象关联的函数,在对象即将被回收时调用。Lua使用__gc元方法实现终结器。

问题是,在某些情况下,当终结器被调用时,对象必须仍然存活。《Lua编程》用以下示例解释了这个问题:

A = {x = "this is A"}
B = {f = A}
setmetatable(B, {__gc = function (o) print(o.f.x) end})
A, B = nil
collectgarbage() --> this is A

B的终结器访问了A,因此在B的终结化之前,A不能被回收。Lua必须在运行该终结器之前复活BA

复活是调用collectgarbage两次的原因:

由于复活的存在,带有终结器的对象会分两个阶段被回收。当收集器第一次检测到带有终结器的对象不可达时,收集器会复活该对象并将其排队进行终结化。一旦终结器运行完毕,Lua将该对象标记为已终结。下一次收集器检测到该对象不可达时,将删除该对象。如果您要确保程序中的所有垃圾都已实际释放,必须调用 collectgarbage两次;第二次调用将删除在第一次调用期间终结器已运行的对象。

2015-02-04 11:34:46