On 2026-01-29 16:38, 'Lars Müller' via lua-l wrote:
> This is an unfortunate consequence of the requirement that items can
> be deleted from a table while it is iterated. This means that you more
> or less need to avoid a rehash, because that would change the
> iteration order while you are iterating.
>
> A trick to permit a (shrinking) rehash is to rawset a *new* entry,
> e.g. using a fresh table as key (by reference), then deleting that
> entry afterwards. But this need not work, it depends on whether the
> implementation decides to shrink.
>
> I'm also not a fan of this behavior because it means that the "garbage
> collection" is unintuitive: There is no guarantee that after a full
> collection, the memory usage is in any way proportional to the actual
> observable size of the data being stored.
>
> The solution Lua currently wants you to use is to replace the table if
> you drop most of its contents and need the memory back. Alternatively,
> Lua could fix this, even while allowing the deletion of elements
> during iteration, but a more sophisticated, likely less efficient
> table representation would be required for this.
>
> I wrote at a bit more length about this in a blog post:
> <
https://luatic.dev/posts/2025-04-12-lua-misconceptions/#setting-elements-to-nil-frees-up-space>
>
>
> - Lars
Thanks for link to your essay Lars, I enjoyed reading it much!
However I don't understand how and why your proposed hack can force
table rehash:
local allow_rehash
do
local key = {} -- can't be in the table, because table equality is
by reference
function allow_rehash(t)
rawset(t, key, true) -- following this, lua is allowed to rehash t
rawset(t, key, nil) -- restore t to original state
end
end
Tried it and saw no effect.
And I share opinion that collectgarbage() is not intuitive. It does
nothing for tables with nils occupying all memory.
I would join to Rett's proposal for table garbage-collect/shrink/rehash
function.
Maybe add one more ad-hoc string argument to collectgarbage()?
collectgarbage('table', t)
Forces rehash of table <t>. Frees memory by not storing empty slots.
Side effect is keys sequence reordering so do not mix with
next()/pairs().
-- Martin