Skip to content

垃圾回收

垃圾回收是 Lua 中自动管理内存的机制,它会自动回收不再使用的内存。本章节将介绍 Lua 中垃圾回收的基本原理和使用方法。

垃圾回收的基本原理

Lua 使用自动垃圾回收机制来管理内存。当一个值不再被任何变量引用时,它就会被垃圾回收器回收,释放占用的内存。

垃圾回收的触发时机

Lua 的垃圾回收器会在以下情况下触发:

  1. 当内存使用量达到一定阈值时
  2. 当显式调用 collectgarbage 函数时

collectgarbage 函数

collectgarbage 函数用于控制垃圾回收器的行为:

lua
-- 强制进行一次垃圾回收
collectgarbage("collect")

-- 停止垃圾回收
collectgarbage("stop")

-- 重启垃圾回收
collectgarbage("restart")

-- 进行一次增量垃圾回收,返回未回收的内存大小
local bytes = collectgarbage("step")

-- 获取当前内存使用量
local bytes = collectgarbage("count")

-- 设置垃圾回收的阈值
collectgarbage("setpause", 100)

-- 设置垃圾回收的步进因子
collectgarbage("setstepmul", 200)

垃圾回收的模式

Lua 5.2 及以上版本支持两种垃圾回收模式:

  1. 增量模式:垃圾回收过程分多个步骤进行,不会阻塞程序的执行
  2. ** generational 模式**:基于对象的生命周期进行垃圾回收,更高效

弱引用表

弱引用表是一种特殊的表,它不会阻止垃圾回收器回收表中的键或值:

弱引用键

lua
local weakKey = setmetatable({}, {__mode = "k"})
local key = {}
weakKey[key] = "value"
key = nil  -- 键被设为 nil
collectgarbage("collect")  -- 强制垃圾回收
for k, v in pairs(weakKey) do
  print(k, v)  -- 不会输出任何内容,因为键已被回收
end

弱引用值

lua
local weakValue = setmetatable({}, {__mode = "v"})
local value = {}
weakValue["key"] = value
value = nil  -- 值被设为 nil
collectgarbage("collect")  -- 强制垃圾回收
for k, v in pairs(weakValue) do
  print(k, v)  -- 不会输出任何内容,因为值已被回收
end

弱引用键和值

lua
local weakKeyValue = setmetatable({}, {__mode = "kv"})
local key = {}
local value = {}
weakKeyValue[key] = value
key = nil  -- 键被设为 nil
value = nil  -- 值被设为 nil
collectgarbage("collect")  -- 强制垃圾回收
for k, v in pairs(weakKeyValue) do
  print(k, v)  -- 不会输出任何内容,因为键和值都已被回收
end

垃圾回收的应用

示例 1:缓存

使用弱引用表可以创建一个自动清理的缓存:

lua
local cache = setmetatable({}, {__mode = "v"})

function getValue(key)
  if cache[key] then
    return cache[key]
  end
  local value = expensiveOperation(key)
  cache[key] = value
  return value
end

示例 2:对象池

使用弱引用表可以创建一个对象池,自动回收不再使用的对象:

lua
local objectPool = setmetatable({}, {__mode = "v"})

function createObject()
  for obj in pairs(objectPool) do
    objectPool[obj] = nil
    return obj
  end
  return {}
end

function releaseObject(obj)
  -- 重置对象状态
  for k in pairs(obj) do
    obj[k] = nil
  end
  objectPool[obj] = true
end

-- 使用对象
local obj1 = createObject()
local obj2 = createObject()

-- 释放对象
releaseObject(obj1)
releaseObject(obj2)

-- 再次创建对象,会重用之前的对象
local obj3 = createObject()
local obj4 = createObject()

print(obj1 == obj3)  -- 输出 true
print(obj2 == obj4)  -- 输出 true

示例 3:事件监听器

使用弱引用表可以创建一个自动清理的事件监听器系统:

lua
local listeners = setmetatable({}, {__mode = "k"})

function addListener(event, listener)
  if not listeners[event] then
    listeners[event] = setmetatable({}, {__mode = "k"})
  end
  listeners[event][listener] = true
end

function removeListener(event, listener)
  if listeners[event] then
    listeners[event][listener] = nil
  end
end

function triggerEvent(event, ...)
  if listeners[event] then
    for listener in pairs(listeners[event]) do
      listener(...)
    end
  end
end

-- 使用事件监听器
local listener = function(message)
  print("收到消息:" .. message)
end

addListener("message", listener)
triggerEvent("message", "Hello, Lua!")

listener = nil  -- 监听器被设为 nil
collectgarbage("collect")  -- 强制垃圾回收
triggerEvent("message", "Hello again!")  -- 不会输出任何内容,因为监听器已被回收

垃圾回收的性能优化

  1. 减少表的创建:频繁创建和销毁表会增加垃圾回收的负担
  2. 使用对象池:重用对象而不是频繁创建新对象
  3. 合理设置垃圾回收参数:根据程序的特点调整 setpausesetstepmul 参数
  4. 使用弱引用表:对于临时对象,使用弱引用表可以让垃圾回收器自动回收它们
  5. 避免循环引用:循环引用会导致垃圾回收器无法回收这些对象

循环引用的处理

Lua 的垃圾回收器可以处理循环引用:

lua
local a = {}
local b = {}
a.b = b
b.a = a

a = nil
b = nil

collectgarbage("collect")  -- 强制垃圾回收
-- a 和 b 会被垃圾回收器回收

小结

本章节介绍了 Lua 中垃圾回收的基本原理、触发时机、collectgarbage 函数、弱引用表和垃圾回收的应用。掌握这些内容,对于编写高效的 Lua 程序非常重要。