当使用多个相同名称的本地变量时的垃圾收集

考虑以下代码:

local v = "hello"
a = function() print(v) end
local v = "world"
b = function() print(v) end
a()
b()

输出为:

hello
world

通常,我期望hello一旦被world覆盖就会被标记为垃圾收集。然而,由于a引用了hello,我认为只要a还在,hello就不会被垃圾收集。因此,执行

a = nil

会将ahello都标记为垃圾收集。对吗?

点赞
用户2858170
用户2858170

通常情况下,我期望当 "hello" 被 "world" 覆盖时就会被标记为待回收的垃圾。

但事实上 "hello" 并没有被 "world" 覆盖。它只是没有被变量 v 引用了。

当执行 a = nil 时,函数值就会被标记为垃圾并被回收,因为它是唯一的引用。

当函数值被删除后,就没有任何东西指向 "hello" 了,所以它也会被回收掉。

2021-01-07 12:25:23
用户3574628
用户3574628

同名的两个局部变量在技术上是两个不同的变量。变量的作用域持续到代码块结束。最大的问题是垃圾回收器是否能够检测到被遮蔽的变量。如果不能,则第一个 v 将一直占用内存,直到块结束。

在下面的代码中,我使用表而不是字符串。由于字符串字面量嵌入在字节码中,使用字符串会使内存使用更难以看到。

local function showMemory()
  -- 进行一次完整的垃圾回收周期,以获得干净的内存读数。
  collectgarbage()
  -- 以字节为单位显示内存使用情况。
  print(collectgarbage'count' * 1024)
end

showMemory()
local v = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
showMemory()
local v = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
showMemory()

输出结果:

24003.0
24291.0
24507.0

第一个 v 添加了 288 个字节的内存使用。第二个 v 添加了 216 个字节的内存使用。

现在,让我们看看当我们使用 do - end 强制使第一个 v 超出作用域时会发生什么:

local function showMemory()
  collectgarbage()
  print(collectgarbage'count' * 1024)
end

showMemory()
do
  local v = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  showMemory()
end
showMemory()
local v = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
showMemory()

输出结果:

24019.0
24307.0
24091.0
24307.0

在这里,我们可以看到每个 v 声明后内存使用情况完全相同。这清楚地表明,两个 v 是否在同一作用域中有很大的不同。

由此,我得出结论,声明一个局部变量 不会 导致同名的先前变量被垃圾回收。

2021-01-07 16:51:07