Will any of these code cause memory leaks?

I’m not entirely sure on how references to objects can cause memory leaks. Does any of these code below have memory leaks. If not what causes memory leaks on references and why?

Example 1

local Part = workspace.Part

local function PrintName()
     print(Part.Name)
end

PrintName()

Example 2

local function PrintName()
      local Part = workspace.Part

      if Part.Name == "Part" then
            local OtherPart = workspace.OtherPart
            print(OtherPart.Name)
      end

      print(Part.Name)
end

PrintName()

Example 3

local function PrintName()
      local Part = workspace.Part

      if Part.Name == "Part" then
            do
                  local OtherPart = workspace.OtherPart
                  print(OtherPart.Name)
            end
      end

      print(Part.Name)
end

PrintName()

Because PrintName internally references Part, if Part is destroyed it will never truly get garbage collected because there is always a hanging reference to Part through PrintName.

You could expect there to be a memoryleak? I’m not entirely sure because I never dabbled in attempting to figure out how Lua garbage collection actually works.

Based what I said off this post: PSA: Connections can memory leak Instances!
Which shows that references to an object in functions will cause a leak.

1 Like

Both the second and third example set the reference within the function, so they shouldn’t leak. It’s creating a new reference every time that the function runs. The first one might, because it still holds a reference to Part that is used.

9 Likes

How would I prevent the memory leak caused by the first example?
Is there a way to de-reference it within the function?

If you drop the Part variable it becomes nil in the function as wel.

local Part = workspace.Part

local function PrintName()
     print(Part.Name)
end

Part = nil
PrintName() --// attempt to index a nil value with '.Name' or something
1 Like

Just set Part to nil at some point. You can check for its AncestryChanged event to see when it gets removed from the workspace, and set the variable to nil then.

5 Likes

Might not be relevant to the question, but its good to know;
When you reference tables and objects and store them in variables, the table / object cannot be garbage collected because there is something somewhere pointing to it. The object / table will not be completely cleaned up until nothing is referring to it.


When you set a variable to a table, it doesn’t copy the table:

local a = {"a", "b", "c"} -- create table a
local b = a -- b references a
a[1] = "d"
print(b[1]) -- prints "d" because b references the table and doesnt copy it

However, if you were to do the same with an integer, it copies:

local a = 4 -- Integer
local b = a -- Copies the value of a at this time
a = 5 -- Changing a does not change b
print(b) -- prints 4 because it simply copied a
2 Likes

In a case where say I use a spawn/coroutine inside the same function “PrintName” and reference another part within the new spawn/coroutine function, does it create the reference in a new scope and gets garbage collected as well?

I’m not sure this is a good example. You’re completely overwriting the variable here. It’s not like the variable is tied to the string. And strings are indeed passed by reference, and are immutable. So it’s hard to show reference this way.

3 Likes

I updated the example with integers :+1:

1 Like

The first one won’t cause a memory leak, the argument passed to print is cleared when the function print ends.

When you reference tables and objects and store them in variables, the table / object cannot be garbage collected because there is something somewhere pointing to it. The object / table will not be completely cleaned up until nothing is referring to it.

Even if that table has strong references, it will be garbage collected since that “strong reference” is cleared assuming its allocated on the stack. If on the heap, you would have to remove that strong reference manually.

do 
    local a = {"a", "b", "c"} 
    local b = a -- a now has a strong reference to its value, b
end
-- the (do) stack ends, a and b are now nil (both of them are removed from memory)
-- when the stack ends, b is garbage collected since it has no strong references.
-- when b is nil, a has no strong references and is garbage collected as well
1 Like