Skip to content
Weak Tables and Caches
step 1/5

Reading — step 1 of 5

Learn

~1 min readClosures and Tables

Lua tables hold strong references by default — keys and values are kept alive as long as the table is. Weak tables let GC collect them.

local cache = {}
setmetatable(cache, { __mode = "k" })   -- keys are weak

__mode modes:

  • "k" — keys are weak
  • "v" — values are weak
  • "kv" — both

Weak-key table: when a key is no longer referenced elsewhere, GC removes the entry.

local cache = setmetatable({}, { __mode = "k" })

local user = {name = "Ada"}
cache[user] = "some computed data"

user = nil      -- the original reference is gone
collectgarbage()
-- The cache entry is also gone (key was weakly held)

Use cases:

Per-object metadata without leaking:

local metadata = setmetatable({}, { __mode = "k" })
function tag(obj, info)
    metadata[obj] = info
end

When obj is collected, its metadata vanishes too — no manual cleanup.

Memoization with weak values:

local computed = setmetatable({}, { __mode = "v" })

function expensive(key)
    if computed[key] then return computed[key] end
    local result = doExpensiveWork(key)
    computed[key] = result
    return result
end

Values can be GC'd when no other references exist — natural cache eviction.

Tables themselves can be weak — singleton registries with auto-cleanup:

local connections = setmetatable({}, { __mode = "v" })
function register(conn)
    connections[conn.id] = conn
end

Caveats:

  • Booleans, numbers, strings are NEVER collected — weak entries with these as keys don't get cleaned up
  • GC is non-deterministic — don't rely on specific cleanup timing
  • Use collectgarbage() to force a full collection (tests, benchmarks)

setmetatable(t, nil) to remove a metatable.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…