Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
8

Can weak values prevent memory leaks from lost table references?

Asked by 5 years ago
Edited 5 years ago

So I know that it's generally bad practice to "clear" a table by just removing reference to it, since that will not free up the memory that the objects in the table still use. For example...

local t = { vec3(), vec3(), vec3() }; -- old t
t = {}; -- new t

...is usually a bad idea. I know that old t will eventually be garbage collected, but from what I understand, the objects inside of it won't be. Just out of curiosity, could this memory leak be prevented by making t a table with weak values? For example, would...

local t = { vec3(), vec3(), vec3() );
setmetatable(t, { __mode = "v" });
t = {};

...be any better than the previous example? Once old t is garbage collected, shouldn't this prevent the objects inside of it from leaking since they have no references? I realize the drawbacks to doing this; it basically makes t a temporary container for the objects before something calls yield or the program finishes and they get gc'd. This isn't a problem for me though.

Edit:

I also realize you can easily free the objects in the table by traversing it and setting all of their references to nil, like such:

for i = 1, #t do
    t[i] = nil;
end;

I'm not asking for an alternative to doing this, because this is just fine. However, I have a specific circumstance where the number of elements in my table may not equal the number of iterations used to traverse the table, and I just don't want to unnecessarily clear the table manually if I don't have to.

1 answer

Log in to vote
7
Answered by
Link150 1355 Badge of Merit Moderation Voter
5 years ago
Edited 5 years ago

First of all, while somewhat correct, your initial claim is not entirely true. You are right in saying that just getting rid of all references to the table will not free up the memory used by its elements. Not right away, anyway. They will eventually all be garbage collected.

So What's Happening?

The garbage collector will collect the table during a first pass, and then its elements on a second pass.

On the first pass the garbage collector doesn't know that the elements no longer have a valid reference. Or rather, it believes there is still a reference to them; the table. Once the table has been collected, the elements become eligible for garbage collection also, but this will not happen until a second pass of the garbage collector.

On Garbage collection

Another thing I'd like to clear up that most people don't know about garbage collection is that objects subject to garbage collection (that is, tables, userdata, functions, threads, strings, etc.) become eligible for collection as soon as there are no valid references to them, but, and this is the important part, the actual garbage collection is not guaranteed to occur immediately when the last reference to these objects ceases to exist. Lua only guarantees that the actual garbage collection may happen at any time following the loss of the last reference to the object. It is possible to trigger a garbage collection manually through collectgarbage("collect") (though this feature is disabled in Roblox both for performance and security reasons), but Lua could decide to trigger a garbage collection at any point during your code's execution because it deems it is running out of memory, for example.

From Lua's documentation: "Lua manages memory automatically by running a garbage collector from time to time to collect all dead objects [...] Lua implements an incremental mark-and-sweep collector."

The latter means that the collection proceeds in small steps that are interleaved with normal computations; i.e, your code's execution.

About Weak Tables

Otherwise, yes, a table with weak values would not count towards the elements' reference count, and so the elements could theoretically be garbage collected along with the table:

"A weak table is a table whose elements are weak references. A weak reference is ignored by the garbage collector. In other words, if the only references to an object are weak references, then the garbage collector will collect this object"

If either the key or the value of an element has no other references then the key-value pair will be removed from the table, and the corresponding key or value garbage collected. But as your code is right now, your elements would become eligible for garbage collection immediately after creation, as they have no other reference besides the table.

However...

What can cause garbage collection problems is tables with indirect circular references. That is, table #1 contains a reference to table #2, which itself contains a reference back to table #1. This kind of structure confuses the garbage collector, as it sees both tables as still having a reference, and for this reason neither tables will ever be garbage collected, resulting in a dead-lock. This can -- and will -- result in memory leaks.

This issue has been addressed in later version of Lua.

0
Thanks for clearing that up! I'll leave a comment on here or I'll be sure to contact you on discord or something if I have any more questions concerning this. ScriptGuider 5640 — 5y
0
I'd be happy to answer any and all questions you may have. :) Link150 1355 — 5y
1
A very good Q/A! I can clearly feel same professionalism feeling that a stack overflow Q/A would. httpOmqCxpcake 70 — 5y
Ad

Answer this question