Does losing a reference to a table with strong references in it leak memory?

Question’s in the title. If that’s unclear, my example scenario should better explain it.

So take this piece of code I wrote, which was pointed out to me privately that it leaks memory.

local accessories = humanoid:GetAccessories()

for _, accessory in ipairs(accessories) do
    if accessoryMeetsDeletionCriteria(accessory) then
        accessory:Destroy()
    end
end

References to Roblox instances are strong by default, so this code sample leaks memory. The obvious solution would be to set the index to nil after destroying it so the instance loses all references and can be properly garbage collected.

local accessories = humanoid:GetAccessories()

for index, accessory in ipairs(accessories) do
    if accessoryMeetsDeletionCriteria(accessory) then
        accessory:Destroy()
        accessories[index] = nil
    end
end

What happens if I lose a reference to the table? Does the table still exist in memory and not get garbage collected, along with the references its holding to Roblox instances? I don’t know if I’m correct or not, but I believe I can lose table references in two ways, so I’m curious to know if either of them leak memory and if I should opt for explicit reference loss like the above.

Both methods are as follows:

-- Forego variable, place table creation directly as the generator argument.
-- This actually doesn't lose the reference, rather, I don't have one at all.
-- The generator may hold a reference to the table though.
for _, accessory in ipairs(humanoid:GetAccessories()) do
    if accessoryMeetsDeletionCriteria(accessory) then
        -- Can't set index to nil because I have no reference to the table
        accessory:Destroy()
    end
end

-- The obvious other alternative is explicitly dropping the reference
local accessories = humanoid:GetAccessories()

for _, accessory in ipairs(accessories) do
    if accessoryMeetsDeletionCriteria(accessory) then
        accessory:Destroy()
    end
end

accessories = nil
2 Likes

When you call a function like :GetChildren or :GetDescendants, and in your case :GetAccessories, Roblox returns a new table. The generator function ipairs and its counterparts don’t keep a reference to the table given so it won’t leak that way. When you are doing something like

x = { { }, function() end, newproxy(false) }
x = nil

The elements inside do get collected eventually since there is no more strong references to them. And of course the table x points to also gets collected.

Not sure if this is related since it’s about using weak references but it’s somewhat relevant to the last code example: Can weak values in a table avoid memory leaks from lost table refs?

2 Likes

So essentially: even if that table has strong references in it, there’s no reference to the table, so the table and the references its holding are eligible for garbage collection. Would that effectively mean that any code samples other than the first don’t leak memory?

2 Likes

yes

3 Likes

Yeah, if accessories = nil fails to gc, it would be an implementation bug, as the userdata’s
__gc metamethod should clean up.

__gc is disabled on Roblox.

Yes, it’s disabled for tables.

Nope! Completely disabled :slight_smile:

local ud = newproxy(true)
local mt = getmetatable(ud)
mt.__gc = function()
	print("GCing userdata")
end

wait(1)
ud = nil
mt = nil
wait(1)

Didn’t print GCing userdata at all.

Are you certain that is the same behavior for Roblox Objects/Instances?

They are also userdatas so the behavior is the exact same. Also their metatable is locked.

I know. But do you know for certain that internally that __gc does not exist? There is no need for it w/ tables or newproxy, but when you actually have have allocations on the native code side, then it’s a different story.

__gc is not even just disabled, it is completely removed. i have a reputable source which i will send you in dms since i dont want to clutter the thread.

Please do–Are you certain it’s not just semantics since it’s not exposed externally?

@sjr04 @Club_Moo

Glad to see you’re interested in the thread, however this discussion isn’t exactly relevant to the question I had asked. It would be much appreciated if further conversation were moved to DMs. :slight_smile:

1 Like