OOP + Memory Management Questions

I recently began working on a project using an OOP approach. Everything has been great until a few days back when I realized that I was creating memory leaks. I’ve been trying to research as much as I can about it, but I still have a several lingering questions.

I know that in order for an object to get garbage collected, all references to the object have to be removed. This is a bit vague though, and I need clarification. Inside of my object, if I set a variable to a part in workspace, would the object be able to get GC’d? What if I don’t ever use the use the variable, would that change anything?

Inside of a few objects I create BindableEvents for various things. These instances are created in the ‘constructor’. Would instantiating an object alone be enough to prevent the object from being destroyed or do I have to have connections hooked up to the event to prevent it from being GCd?

Would having a table reference to other loaded modules in the game prevent the object from being destroyed?

As far as handling whatever needs to be destroyed, I ran across Maid objects. I’ve seen some people say they use them, other people opt to use a :Destroy() method on each object. Is it just a personal preference?

I also have another question regarding “do end”. From what I got, it creates a new scope so it’s useful when you want to define variables in it temporarily and then want those variables to be GC’d. Is that correct? An example would be:

local x = object;
-- The object can not get GC'd because it has a reference.

    local x = object;
-- The variable would be GC'd, allowing the object to also get GC'd(assuming it has no other references)

Is this correct?

My final question involves the following code:

local module = require(game.ReplicatedStorage.Object);

local collection = {};

local refCounter = {}
setmetatable(refCounter, {__mode = "v"})

	local object = module.new();

	refCounter[1] = object;
	collection[1] = object;




collection[1] = nil;


print(#refCounter); -- Prints 0

This prints out 0 which I don’t understand because I still have an event hooked up to the object. Shouldn’t the event prevent the object from being GC’d?

Thank you to anybody who read all of this. As you can see I’m all over the place with my understanding of all of it. I’m trying to piece as much information together as I can, but figured I needed to post here to get more help. I’ll take as much knowledge as I can get!

Strong references are things like variables, table references, and upvalue references. The Maid vs manual management thing is really up to preference. As long as you have some kind of strong reference to an object from within the Lua end, it won’t be collected. The GC also doesn’t come into play right away, it comes in cycles. Some cycles can dealloc more, some less.

Even if your object has references, as long as it can’t be reached from Lua code or accessed from Roblox code it will GC, so things like cyclic tables are handled.

This brings me to do end, it’s kinda useless for GC purposes. More likely than not, if your code isn’t blocking, the variable reference will be gone faster than you can count even if you opt out of using do end to enclose it. Scopes also affect upvalue behavior, but that’s usually irrelevant unless you have weird edge cases where you need it.

As for your code example, without knowing what your object is doing I can’t really elaborate on it.

Thanks for the information!

Here is the object code:

local Object = {};
Object.__index = Object;

function Object.new()
	local self = setmetatable({}, Object);
	self.CustomEvent = Instance.new("BindableEvent", game.Workspace);
	return self;

function Object:FireEvent()

return Object;

Your event is hooked up to the BindableEvent, not your table, so your table has ran out of strong references after you remove it from the other table. This means it should GC as normal.