How can I prevent memory leaks in my game?

So I made a game here which you can check it out in this post. When I was playing it, I discovered some memory leaks that I have to prevent it in order to make my game performant. However, I couldn’t tell what’s wrong with my script that is causing these memory leaks.

Client’s memory leaks:

Server’s memory leaks:

And here is the script I suspect it causing these:
(this script handles the backend of the rocket bullet being shot by the rocket launcher itself after the client fires the remote event by clicking the tool)

RemoteEvent.OnServerEvent:Connect(function(plr,pos,handlePos)
	
	-- settings
	local explosionDisconnected = false
	
	--print("fired")
	local rocket = ServerStorage.Rocket:Clone()
	rocket.Parent = workspace
	--print(rocket.Parent.Name)
	
	-- setting up rocket
	rocket.CFrame = CFrame.new(handlePos,pos)
	
	-- setting velocity
	local direction = (pos - handlePos).Unit
	rocket.BodyVelocity.Velocity = direction * speed
	
	-- setting explosion
	local explosion = Instance.new("Explosion")
	explosion.BlastPressure = 10000	
	explosion.BlastRadius = 13
	
	-- setting up fire
	rocket.Touched:Connect(function(hit)
		if not workspace.Targets:FindFirstChild(hit.Name,true) or hit.Name == "Handle" then -- hit us
			return
		else
			if hit:IsA("BasePart") then hit.Anchored = false end
			local CloneFire = rocket.Fire:Clone()
			explosion.Position = rocket.Position
			explosion.Parent = workspace
			
			-- destroy this part or not?
			local DestroyOrNot = RNG:NextInteger(0,1) == 1 and true
			--print(DestroyOrNot)

			if DestroyOrNot then
				hit:Destroy()
			else
				CloneFire.Parent = hit
			end
			Debris:AddItem(CloneFire,RNG:NextInteger(10,30))
		end
	end)
	
	local explosionConnection
	
	explosionConnection = explosion.Hit:Connect(function(hit)
		if not explosionDisconnected then
			explosionDisconnected = true
			task.defer(function()
				task.wait(explosionTimeout)
				explosionConnection:Disconnect()
			end)
		end
		if not workspace.Targets:FindFirstChild(hit.Name,true) or hit.Name == "Handle" then -- hit us
			return
		else
			if hit:IsA("BasePart") then hit.Anchored = false end
			local CloneFire = Instance.new("Fire")
			CloneFire.Color = Color3.fromRGB(RNG:NextInteger(0,255),RNG:NextInteger(0,255),RNG:NextInteger(0,255))
			CloneFire.SecondaryColor = Color3.fromRGB(RNG:NextInteger(0,255),RNG:NextInteger(0,255),RNG:NextInteger(0,255))
			
			explosion.Position = rocket.Position
			rocket:Destroy()
			explosion.Parent = workspace

			-- destroy this part or not?
			local DestroyOrNot = RNG:NextInteger(0,5) == 1 and true -- 20% chance to destroy
			--print(DestroyOrNot)

			if DestroyOrNot then
				hit:Destroy()
			else
				CloneFire.Parent = hit
			end
			Debris:AddItem(CloneFire,RNG:NextInteger(10,30))
		end
	end)
	
	-- finalizing
	rocket.Parent = workspace
	
	-- timeout if rocket did not reach anything
	Debris:AddItem(rocket,10)
	
	--print("finishd")
end)

Variables speed, RNG, and explosionTimeout are outside the scope of this function, which is a small cause for the memory leaks, but I believe this function is responsible for the majority of the memory leaks.

Please give me any suggestion on how to prevent it. Thanks.

Hello, it is because you do all your effects on the server. Try using :FireAllClients(), what this does is fire stuff on every client instead of the server, so same effect but no memory leaks on the server. But be aware damage and or data changes should always be done on server, else it’s exploitable.

I can’t test this right now unfortunately, but I’m not sure if Debris:AddItem calls :Destroy on the instance. The use of the former is discouraged anyway – so opt for the latter.

@XurySenpai im not sure you understand what a memory leak is…?

1 Like

I understand what a memory leak is :slight_smile: my phrasing was probably incorrect, but doing a lot of stuff on the server takes up a lot of its memory. And if he doesn’t do certain stuff like getting rid of all connections, it’s gonna have an even bigger impact, solely cause it’s on the server. The number one method to not have server memory leaks are to do as much as possible on client. He’s having explosions etc all on the server.

It does:

1 Like

Yeah…you see why I was having doubts? Even if he did do it on the client, the memory leak would just be on the client now – the problem would still persist.

2 Likes

client side memory leak is better than a server side memory leak, think about it the positive way :sunglasses:

um… yeah. What IS a memory leak?

I’ve seen big games such as Roblox bedwars, Work at a Pizza Place, all of them have ZERO UntrackedMemory in their developer console. The way how you say that it’s better confuses me, should I be upset or trust what you said?

The “effects” that makes my rocket bullets fly is nothing more than using BodyVelocity. So should I just use :FireAllClients() which sends the velocity data to all clients?

Memory leak define as a moment that was not been garbage collected, which results in a memory management that fails to get that memory to free up memory space and instead it is still being used even though there isn’t any use for the memory.

You’re creating explosions and fire, those are particles. Just do it on client :angry:

It did reduce a little bit, the server is only facing 90MB memory, while the client is around 130MB memory, however I still see other games have 0MB, is there any more way to do so? Here is the client script that handles the creation of the part and everything, and the remote event should be fired from RemoteEvent:FireAllClients().

RemoteEvent.OnClientEvent:Connect(function(hitPos,handlePos,rocketSpeed,DestroyOrNot)
	local rocketBullet = Instance.new("Part") do
		rocketBullet.Size = Vector3.new(0.319, 0.292, 3.305)
		rocketBullet.BrickColor = BrickColor.new("Really black")
		rocketBullet.Material = Enum.Material.Cobblestone
		rocketBullet.Reflectance = 0.2
		rocketBullet.CFrame = CFrame.new(handlePos,hitPos)
	end
	local fire = Instance.new("Fire") do
		fire.Color = Color3.fromRGB(RNG:NextInteger(0,255),RNG:NextInteger(0,255),RNG:NextInteger(0,255))
		fire.SecondaryColor = Color3.fromRGB(RNG:NextInteger(0,255),RNG:NextInteger(0,255),RNG:NextInteger(0,255))
		fire.Heat = 9
		fire.Size = 6
	end
	local bodyVelocity = Instance.new("BodyVelocity") do
		local direction = (hitPos - handlePos).Unit
		bodyVelocity.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
		bodyVelocity.Velocity = direction * rocketSpeed
	end
	local explosion = Instance.new("Explosion") do
		explosion.BlastRadius = 13
		explosion.BlastPressure = 10000
	end
	
	rocketBullet.Touched:Connect(function(hit)
		local cloneFire
		if not workspace.Targets:FindFirstChild(hit.Name,true) or hit.Name == "Handle" then -- hit us
			return
		else
			if hit:IsA("BasePart") then 
				hit.Anchored = false 
			end
			explosion.Position = rocketBullet.Position
			explosion.Parent = workspace

			if DestroyOrNot then
				hit:Destroy()
			else
				cloneFire = fire:Clone()
				cloneFire.Parent = hit
			end
			if cloneFire ~= nil then Debris:AddItem(cloneFire,RNG:NextInteger(10,30)) end
		end
	end)
	
	explosion.Hit:Connect(function(hit)
		local cloneFire
		if not workspace.Targets:FindFirstChild(hit.Name,true) or hit.Name == "Handle" then -- hit us
			return
		else
			if hit:IsA("BasePart") then 
				hit.Anchored = false
			end
			explosion.Position = rocketBullet.Position
			explosion.Parent = workspace
			
			local ExplosionDestroy = RNG:NextInteger(0,5) == 3 and true
			
			if ExplosionDestroy then
				hit:Destroy()
			else
				cloneFire = fire:Clone()
				cloneFire.Parent = hit
			end
			if cloneFire ~= nil then Debris:AddItem(cloneFire,RNG:NextInteger(10,30)) end
		end
	end)
	
	bodyVelocity.Parent = rocketBullet
	fire.Parent = rocketBullet
	
	rocketBullet.Parent = workspace
	Debris:AddItem(rocketBullet,10)
end)

Now another problem is, I have to literally make every part that was affected by the Touched and Hit events to get their ownership to the client that shoots out the rocket bullet and the rocket bullet detects these parts. I cannot use remote events to send the BasePart to the server script to set the network ownership because one, any instances that act as a data to be sent in remote events will be wiped out while transferring the data. Two, BasePart:SetNetworkOwnership() can only be done on server sided.

Is there any solutions?

one friend recommend me to use maids to avoid memory leaks.

1 Like

Weird, after I tried using Maid, client’s untracked memory is around 60MB, while the server is around 130MB.