Skip to content

Lua 垃圾回收

Lua - 垃圾回收(Garbage Collection)

Section titled “Lua - 垃圾回收(Garbage Collection)”

Lua 具备自动内存管理能力,主要通过垃圾回收器(GC)实现。这意味着对于大多数 Lua 对象,开发者通常无需手动分配和释放内存。

  • 您无需为表(tables)、函数(functions)、字符串(strings)等对象显式地分配内存;Lua 在创建它们时会处理好这一切。
  • 当对象不再需要时,您无需显式地释放其内存。垃圾回收器会识别那些程序不再可访问的“死亡”对象(dead objects),并回收它们占用的内存。

Lua 采用的是一种增量式(incremental)的标记-清除(mark-and-sweep)垃圾回收器。GC 会周期性地运行,查找那些“死亡”的对象——即程序中任何部分(例如,全局变量、栈或寄存器)都无法再访问到的对象。一旦识别出来,这些死亡对象占用的内存就会被回收,可用于未来的内存分配。

所有 Lua 对象(表、函数、用户数据 userdata、协程 threads、字符串)都受垃圾回收机制管理。GC 的操作由两个主要参数控制:

此参数控制垃圾回收器在上一个回收周期完成后,等待多久才开始新的回收周期。它以百分比表示。值为 100(默认值通常是 collectgarbage("setpause", GCTHRESHOLD) 中的 200)意味着回收器会等到已使用的总内存翻倍后,才会启动新的回收周期。较低的值会使 GC 更“激进”(运行更频繁),而较高的值会使其不那么“激进”(运行不频繁,可能会导致更多垃圾在周期之间累积)。默认值为 200。

垃圾回收器步长乘数(setstepmul)

Section titled “垃圾回收器步长乘数(setstepmul)”

此参数控制垃圾回收器相对于内存分配的相对速度。它影响 GC 在每个增量步骤中完成的工作量。较大的值会使 GC 更“激进”,相对于内存分配而言,它会更快地完成工作。较小的值(小于 100)可能会导致 GC 在内存分配非常快速时无法完成一个回收周期。默认值为 200。

Lua 提供了 collectgarbage(opt [, arg]) 函数来与垃圾回收器交互并控制它:

  • collectgarbage("collect"): 执行一次完整的垃圾回收周期。当您知道某个时机适合回收内存时非常有用(例如,在游戏关卡切换期间)。
  • collectgarbage("count"): 返回 Lua 当前使用的总内存,单位为千字节(KB)(以浮点数形式)。
  • collectgarbage("stop"): 停止垃圾回收器。仅在特定场景下有用,应谨慎使用,因为内存将不再自动回收。
  • collectgarbage("restart"): 如果之前已停止,则重启垃圾回收器。
  • collectgarbage("setpause", percentage): 设置垃圾回收器暂停参数。arg 是一个百分比(例如,200 表示 200%)。返回之前的值。
  • collectgarbage("setstepmul", percentage): 设置垃圾回收器步长乘数。arg 是一个百分比(例如,200 表示 200%)。返回之前的值。
  • collectgarbage("step" [, stepsize_kb]): 执行垃圾回收的一个单步。可选的 stepsize_kb 参数影响完成的工作量(依赖于具体实现)。如果该步完成了一个回收周期,则返回 true。
  • collectgarbage("isrunning"): 返回一个布尔值,表示回收器是否正在运行(未停止)。
  • collectgarbage("generational"): (Lua 5.4+) 将 GC 模式更改为分代式(generational)。要求 arg 为 "generational"。
  • collectgarbage("incremental"): (Lua 5.4+) 将 GC 模式更改为增量式(incremental)。要求 arg 为 "incremental",可带可选参数用于设置暂停时间、步长乘数和步进大小。
local initial_memory = collectgarbage("count")
print(string.format("Initial memory usage: %.2f KB", initial_memory))
-- Create some garbage
local large_table = {}
for i = 1, 10000 do
large_table[i] = "Some string data #" .. i
end
local memory_after_allocation = collectgarbage("count")
print(string.format("Memory after allocation: %.2f KB", memory_after_allocation))
-- Make the table unreachable
large_table = nil
local memory_after_nil = collectgarbage("count")
print(string.format("Memory after setting to nil (before GC): %.2f KB", memory_after_nil))
-- Force garbage collection
collectgarbage("collect")
local memory_after_gc = collectgarbage("count")
print(string.format("Memory after explicit GC: %.2f KB", memory_after_gc))

这段代码的输出会因 Lua 版本、操作系统以及 Lua 环境的具体状态而异。不过,您通常会观察到在分配后内存增加,而在执行 collectgarbage("collect") 后内存减少(前提是确实收集了显著的垃圾)。

示例输出(仅供参考):

Initial memory usage: 20.50 KB
Memory after allocation: 750.80 KB
Memory after setting to nil (before GC): 750.85 KB
Memory after explicit GC: 22.10 KB

虽然可以通过 collectgarbage 进行手动控制,但对于大多数应用来说,Lua 的 GC 通常足够高效,无需进行显式调优。不过,了解这些控制方法对于性能要求苛刻的应用或调试内存相关问题时会很有益处。对于大多数 Lua 程序员而言,简单地将不再需要的变量设置为 nil 就足以让 GC 完成其工作。

关于弱引用表(Weak Tables)的说明:Lua 还支持弱引用表,表中的引用不会阻止对象被垃圾回收。这是一个进阶主题,对于缓存或在不创建强引用的情况下将数据与对象关联非常有用。