Memory Management

This is an intermediate tutorial This is an intermediate tutorial. This tutorial discusses memory management. Return to tutorial index

Note:This article is a work in progress. More scenarios need to be added for it to be considered a complete article.

Introduction[edit]

Memory management, is defined as the act of managing computer memory at a system level. The essential requirement of memory management is to provide ways to dynamically allocate portions of memory to programs at their request, and free it for reuse when no longer needed.

Most of the time, you won't need to worry about memory consumption on Roblox. Lua will (for the most part) manage memory for you, as long as you are doing so correctly. However, when working on low-end devices like mobile, its always helpful to consider the performance benefits of optimizing your code's memory usage.

The Basics[edit]

By now, you are probably familiar with how scopes in Lua work. But did you know that local variables get automatically recycled once there are no more references to it?
This is a fancy feature in Lua, known as garbage collection. When an object in memory is no longer being referenced by anything in Lua, the memory being allocated to maintaining that object's existence is released, allowing new data to take its place.

In Roblox, objects work in a similar manner to this. There are 3 primary conditions that prevent objects from being garbage collected:

  1. The object is a descendant of the DataModel.pngDataModel
  2. There is a variable pointing to the object, that is actively being referred to in Lua code.
  3. Another object in the DataModel.pngDataModel is pointing to the object via one of its properties (for example, the Value property of an Value icon.pngObjectValue)

In most cases if any, #2 will be the problem you run into.
When memory is never garbage collected even when its not in use, this is known as a memory leak.

Scenario 1: Tracking Descendants[edit]

Lets say that you want to have an array that gives you access to all of the descendants in the workspace on demand. You could just write a script like this:

local descendants = {}
 
local function onDescendantAdded(descendant)
	table.insert(descendants,descendant)
end
 
workspace.DescendantAdded:connect(onDescendantAdded)


However, there is a problem with this code. Since we have an active connection in this script, the contents of the descendants array will remain active, until the connection in the script is disconnected. Contrary to popular belief, signals like DescendantAdded won't actually disconnect until the object it is associated with is removed, regardless if the script object is still present in the game. Since we are working with the Workspace icon.pngWorkspace, this Lua thread will remain active as long as the game is running.

With all of that said, any object that is put into the descendants table will not get garbage collected, and thats a problem.
However, this problem can be solved, with just a little more code.

local descendants = {}
 
local function onDescendantAdded(descendant)
	descendants[descendant] = true
end
 
local function onDescendantRemoving(descendant)
	if descendants[descendant] then
		descendants[descendant] = nil
	end
end
 
workspace.DescendantAdded:connect(onDescendantAdded)
workspace.DescendantRemoving:connect(onDescendantRemoving)


As you can see, we changed our method of tracking objects. Instead of adding them as values to the descendants table, we changed them into a pointer value for a boolean. This allows us to quickly point to the object on the array, and set its value to nil when DescendantRemoving gets fired, and thus this script won't cause memory leaks, because its not keeping a pointer reference to the object.