So I was testing around for any memory leaks recently and I came across some pretty odd behavior. All these code examples I’m about to go over are done in a single Script in an empty place.
Make a weak table. Put a value in it. Wait an arbitrary amount of time and check the weak table. If it no longer exists its been garbage collected.
local gc_table = setmetatable({}, {__mode = "v"})
gc_table[1] = { "eek!"}
wait(5)
print(gc_table)
This works how I imagine it does. The GC step runs at some point between yielding control of the thread and returning back to it.
Now try this?
local gc_table = setmetatable({}, {__mode = "v"})
wait(5) -- wait before setting a value in the gc_table
gc_table[1] = { "eek!"}
wait(5) -- gc step run when?
print(gc_table) --- it didnt run? where is the strong ref otherwise?
This prints out the entry? I understand that the garbage collector runs somewhat periodically and it can be arbitrary. But I’ve run this many many times with wildly different durations for the second wait call and it changes nothing. I tried using all the old and new scheduler functions and running some code before and after and throwing it in loops and putting it all in a coroutine and all that jazz to see if I could get the behavior to change but nothing seemed to work except this:
local gc_table = setmetatable({}, {__mode = "v"})
wait(1) -- wait not 5 seconds but 1???
gc_table[1] = { "eek!"}
wait(5) -- gc step run when?
print(gc_table) --- why does it run now?
So now I have concluded that I have literally no idea what’s going on internally. I slept on it thinking I was seeing things but I am no longer asleep and this behavior is still here. If I make the first wait duration arbitrarily small enough it will run the GC step? Can someone explain what exactly I am failing to see here or is this just an engine bug?
Thanks in advance.