Hard Written By TheRobloxPlayer2509

Connection and Memory Leak Created on: 22-08-2019

Advanced topic, decent scripting knowledge and experience required.

Intro

Have you ever accidently pressed F9 and then a big menu with a lot of information pops up? That's called the Developer Console. If you go to the Memory tab and scroll all the way to the bottom, you can see "UntrackedMemory". If you're viewing the Client Memory, it'll always be 0. But if you view the Server's Memory, you can see it fluctuating.

You may be asking: "Why should we care about all of these information?". Well, here's the catch. If a server uses too much memory, it'll start lagging. And if the memory usage is so great, it'll crash.

You may also be asking: "Why should we care about UntrackedMemory the most?". Well, UntrackedMemory is the most difficult to handle and A LOT of game developers suffer from it. I have seen many famous game developers fail to solve the UntrackedMemory problem and have their game extremely laggy. I'm here to tell you how UntrackedMemory generates and how to prevent it.

How is it produced?

It's actually really simple to generate UntrackedMemory. It can be but not limited to:

workspace.Part.Parent = nil -- Setting an instance's Parent to nil. (Not destroying)

workspace.MeshPart:Destroy() -- Destroying a Mesh/MeshPart

game.Players.PlayerAdded:Connect(function(Player) -- Connections (The biggest issue)
    print("A player has joined!")
end)

local New_Part = Instance.new("Part", workspace)
New_Part:Destroy()
wait(1)
print(New_Part.Name) -- Referencing a destroyed object

Side note: Destroying a MeshPart might no longer generate UntrackedMemory because roblox is constantly updating.

In this case, we'll focus on Connections. (Because this is where people have the most issue with)

You should know what a Connection is since this is an advanced topic.

Connections

Connections are useful, they are used in almost every script. Even a simple touch detector has a connection in it.

This is a simple touch detector:

workspace.Part.Touched:Connect(function(Object)
    print("My part is touched!")
end)

You may think there's nothing wrong with it. Then you're completely wrong.

Connections take up quite a lot of memory space. And if you make a lot of them, things will go wrong.

Take this as an example:

while wait(1) do
    local New_Part = Instance.new("Part", workspace)
	New_Part.Position = Vector3.new(0, 5, 0)
	New_Part.Anchored = true
	New_Part.Touched:Connect(function(Object)
	    print("I have been touched!")
	end)
end

This simple script will generate 1 connection every second. If you wait long enough, the server may start lagging. It's not because of the amount of instances, it's because of the high UntrackedMemory. By that point, if you press F9 and check the server's Memory, it'll shock you.

The UntrackedMemory can go up to several thousands of MB, which is more than enough to crash a server. That's insane.

Preventing Memory Leak

So how do we prevent UntrackedMemory? It's actually quite simple.

local New_Part = Instance.new("Part", workspace)
local cn; cn = New_Part.Touched:Connect(function(Object)
    print("I have been touched!")
	cn:Disconnect() -- Disconnecting a connection when it's not needed anymore.
end)

local New_Part2 = Instance.new("Part", workspace)
New_Part2.Touched:Connect(function(Object)
    print("I have been touched!")
	New_Part2:Destroy() -- Destroying the part that's connected will automatically disconnect the connection.
end)

while wait(1) do
    local New_MeshPart = Instance.new("MeshPart", workspace)
	New_MeshPart.Touched:Connect(function(Object)
        -- Avoid putting connections into a loop.
	end)
end

local New_Part3 = Instance.new("Part", workspace)
New_Part3.Touched:Connect(function(Object)
    local New_Part4 - Instance.new("Part", workspace)
	New_Part4.Touched:Connect(function(Object)
	    -- Avoid connection inside a connection!
	end)
end)

spawn(function()
    -- Avoid creating seperate threads!
	while wait(1) do
	    print("I am a string!")
	    -- Especially with while loops in them!
	end
end)

coroutine.wrap(function()
    -- Avoid creating seperate threads!
end)()

do
    -- Avoid creating seperate threads!
end

-- Always put local before variables otherwise they may not be picked up by Lua Garbage Collector!
local This_Is_A_Variable = true
Do_Not_Be_This_Guy = false -- Do not be this guy!

Additional methods:

  • Using a good scripting style. (Keeping the code tidy, using comments correctly and more)
  • Avoid using deprecated methods.
  • Use as few scripts as possible, make good use of ModuleScript.

Conclusion

This concludes the topic, I hope it helped. Now be more aware of Memory Leak!

Hard Written By TheRobloxPlayer2509

See TheRobloxPlayer2509's profile on Roblox

Discussion