How to reduce high Memory Usage on my Egg hatching script?

Hi, My game when it first log in it got 700-900MB of memory usage, but when the egg is hatched for more than 12 hours straight the memory usage will jump to 3000-4000MB and some player even had their roblox application crashed due to lack of pc memory.

Below is the script

local turnOnOffEggBuyFrame = function(on, eggName)
	if on == true and eggName ~= nil then
		if on == true and eggName == "Basic Egg" then
			for _, v in pairs(eggUIFolder.CapsuleButtons:GetChildren()) do
				if v:IsA("BillboardGui") then v.Enabled = true end
			end
			for _, v in pairs(eggUIFolder.CapsulePetFrames:GetChildren()) do
				if v:IsA("BillboardGui") then v.Enabled = true end
			end
		else
			local buttons = eggUIFolder.CapsuleButtons:FindFirstChild(eggName)
			local petFrame = eggUIFolder.CapsulePetFrames:FindFirstChild(eggName)
			if buttons and petFrame then
				buttons.Enabled,petFrame.Enabled = true,true
			end
		end
	else
		for _, v in pairs(eggUIFolder.CapsuleButtons:GetChildren()) do
			if v:IsA("BillboardGui") then v.Enabled = false end
		end
		for _, v in pairs(eggUIFolder.CapsulePetFrames:GetChildren()) do
			if v:IsA("BillboardGui") then v.Enabled = false end
		end
	end
end

local UIToTurnOn = mainUIMod.UIToTurnOn
local UIToTurnOff = mainUIMod.UIToTurnOff

local turnOnOffOtherUI = function(on)
	if on == true then
		for _, v in pairs(main:GetChildren()) do
			if UIToTurnOn[v.Name] == true then
				if v.Name == "SafeZone" then
					safeZoneMod.ChangeSafeZoneUI()
				elseif v.Name == "GroupBoost" then
					groupBoostMod.GroupBoostChange()
				elseif v.Name == "VipServerBoost" then
					groupBoostMod.VipServerBoostChange()
				elseif v.ClassName == "ScreenGui" then
					v.Enabled = true
				else
					v.Visible = true
				end
			end
		end
		
		if plrGUI:FindFirstChild("DevPanel") ~= nil then
			plrGUI:FindFirstChild("DevPanel").Button.Visible = true
		end
		
		if plrGUI:FindFirstChild("YoutubePanel") ~= nil then
			plrGUI:FindFirstChild("YoutubePanel").Button.Visible = true
		end
	else
		for _, v in pairs(main:GetChildren()) do
			if UIToTurnOff[v.Name] == true then
				if v.ClassName == "ScreenGui" then
					v.Enabled = false
				else
					v.Visible = false
				end
			end
		end
		if plrGUI:FindFirstChild("DevPanel") ~= nil then
			plrGUI:FindFirstChild("DevPanel").Button.Visible = false
		end
		if plrGUI:FindFirstChild("YoutubePanel") ~= nil then
			plrGUI:FindFirstChild("YoutubePanel").Button.Visible = false
		end
	end
end

local gameInterfaceUI = function(on)
	game:GetService("StarterGui"):SetCoreGuiEnabled(Enum.CoreGuiType.Backpack, on)
	game:GetService("StarterGui"):SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, on)
end

local getGUI = function(m)
	local gui = m.PrimaryPart:FindFirstChildWhichIsA("BillboardGui")
	if gui and gui.Name ~= "XpDisplay" then
		return gui
	end
end

local turnOnOffPetNameTags = function(plr, on)
	if clientPets:FindFirstChild(plr.Name) ~= nil then
		for _, petModel in pairs(clientPets[plr.Name]:GetChildren()) do
			if petModel ~= nil and petModel.PrimaryPart ~= nil then
				local gui = getGUI(petModel)
				if gui ~= nil then
					gui.Enabled = on
				end
			end
		end
	end
end

function IsEffectsEnabled()
	local lowQuality = script.Parent.Parent.Parent.UI.Settings.LowModeOn
	return not lowQuality.Value
end

local function getEggSpeedHatchBoost(plr)
	-- Added by Azizi 22 Jan 2022 (If have Fast Egg Open)
	local hasFastEgg = gamepassMod.CheckPass(plr, "FastEggOpen") --Check if player has Fast Egg Open Gamepass
	if hasFastEgg then
		return 3
	else
		return 1.5
	end
end
EggService.GetEggSpeedHatchBoost = getEggSpeedHatchBoost

local setPlrSpeed = function(plr)
	if not plr then
		warn("Player does not exist! \"" .. tostring(plr) .. "\"")
		return
	end
	if not plr.Character then
		warn("Character does not exist for \"" .. tostring(plr.Name) .. "\" (\"" .. tostring(plr.Character) .. "\")!")
	end
	local character = plr.Character
	if not plr:FindFirstChild("Skills") then
		warn("Cannot find folder named \"Skills\" in player")
		return false
	end
	local skills = plr:WaitForChild("Skills")
	local speedVal = 16
	if skills:FindFirstChild("Speed1") and skills:FindFirstChild("Speed2") and skills:FindFirstChild("Speed3") then
		local mul = 16 * .3
		speedVal = 16 + mul
	elseif skills:FindFirstChild("Speed1") and skills:FindFirstChild("Speed2") then
		local mul = 16 * .15
		speedVal = 16 + mul
	elseif skills:FindFirstChild("Speed1") then	
		local mul = 16 * .05
		speedVal = 16 + mul
	end
	
	if gamepassMod.CheckPlrPass(plr, "2xSpeed") == true then
		speedVal = speedVal * 2
	end
	
	return speedVal
end

local function returnTweenData(data)
	return {
		Time = data[1] or 1;
		EasingStyle = data[2] or "Linear";
		EasingDirection = data[3] or "Out";
		Reverses = false;
		DelayTime = 0;
		RepeatCount = 1;
		StepType = "Heartbeat";
		Goal = data[4] or {};
	}
end

local function playTween_Func(object, data)
	local tweenData = returnTweenData(data)
	local tween = boatTween:Create(object, tweenData)
	tween:Play()
	tween.Completed:Connect(function()
		tween:Destroy()
	end)
end

function EggService:HatchEgg(eggName, petName, newPet, offset, petIndex, shiny, auto)
	if petIndex == nil then petIndex = 2 end
	offset = offset or cameraOffset
	local player = plrs.LocalPlayer
	local gui = main
	
	-- ######## Added for Luck Combo Display and Pet Percentage ######### --
	local luckCombo = player.PlayerInfo.LuckCombo.Value
	if luckCombo >= 8000 then
		luckComboTextUI.Text = "Combo Luck +30% (MAX)"
	elseif luckCombo >= 6500 then
		luckComboTextUI.Text = "Combo Luck +25% ("..tostring(luckCombo).."/8000)"
	elseif luckCombo >= 5000 then
		luckComboTextUI.Text = "Combo Luck +20% ("..tostring(luckCombo).."/6500)"
	elseif luckCombo >= 3500 then
		luckComboTextUI.Text = "Combo Luck +16% ("..tostring(luckCombo).."/5000)"
	elseif luckCombo >= 2000 then
		luckComboTextUI.Text = "Combo Luck +12% ("..tostring(luckCombo).."/3500)"
	elseif luckCombo >= 1000 then
		luckComboTextUI.Text = "Combo Luck +8% ("..tostring(luckCombo).."/2000)"
	elseif luckCombo >= 500 then
		luckComboTextUI.Text = "Combo Luck +6% ("..tostring(luckCombo).."/1000)"
	elseif luckCombo >= 250 then
		luckComboTextUI.Text = "Combo Luck +4% ("..tostring(luckCombo).."/500)"
	elseif luckCombo >= 100 then
		luckComboTextUI.Text = "Combo Luck +2% ("..tostring(luckCombo).."/250)"
	else
		luckComboTextUI.Text = "Combo Luck +0% ("..tostring(luckCombo).."/100)"
	end
	luckComboTextUI.Visible = true
	
	local v = workspace.Eggs:FindFirstChild(eggName)
	if eggs[v.Name] == nil then
		eggs[v.Name] = v
	end
	showPetsInEggsMod.ChangePetPercentages(player, eggs)
	-- ######## Added for Luck Combo Display and Pet Percentage ######### --
	
	--print(eggName, petName, getPetName(petName), newPet, offset, petIndex, shiny, auto)
	
	-- turn off all UI and top roblox buttons
	turnOnOffOtherUI(false) turnOnOffEggBuyFrame(false) gameInterfaceUI(false) turnOnOffPetNameTags(player, false)
	
	-- set egg gui text
	local speedDiv = getEggSpeedHatchBoost(player)
	local hatchFrame
	local function setHatchFrameTexts()
		hatchFrame = petIndex and (gui:FindFirstChild("MultiPetHatch") or script.MultiPetHatch:Clone()) or script.PetHatch:Clone()
		if petIndex == nil then
			hatchFrame.BackgroundTransparency = 1
			hatchFrame.Pet.ImageTransparency = 1
			hatchFrame.Pet.PetName.TextTransparency = 1
			hatchFrame.Pet.PetName.TextStrokeTransparency = 1
			hatchFrame.Pet.Rarity.TextTransparency = 1
			hatchFrame.Pet.Rarity.TextStrokeTransparency = 1
			hatchFrame.Pet.Discovered.TextTransparency = 1
			hatchFrame.Pet.Discovered.TextStrokeTransparency = 1
			hatchFrame.Skip.Visible = false
			local rName,typ = getPetName(petName)
			local rarity = getRarity(rName)
			hatchFrame.Pet.Rarity.Text = rarity
			hatchFrame.Pet.PetName.Text = tostring(petName)
			if typ == "Shiny" then
				hatchFrame.Pet.PetName.TextColor3 = Color3.new(1, 1, 0)
			else
				hatchFrame.Pet.PetName.TextColor3 = Color3.new(1, 1, 1)
			end
			local rarityColor = rarityColors:FindFirstChild(tostring(rarity)).Display
			hatchFrame.Pet.Rarity.TextColor3 = rarityColor.Value
			hatchFrame.Parent = gui
		else
			if not hatchFrame.Parent then
				hatchFrame.Parent = gui
				hatchFrame.Skip.Visible = false
			end
			for a, b in pairs(hatchFrame:GetChildren()) do
				if b.Name ~= "Skip" then
					b.BackgroundTransparency = 1
					b.ImageTransparency = 1
					b.PetName.TextTransparency = 1
					b.PetName.TextStrokeTransparency = 1
					b.Rarity.TextTransparency = 1
					b.Rarity.TextStrokeTransparency = 1
					b.Discovered.TextTransparency = 1
					b.Discovered.TextStrokeTransparency = 1
				end
			end
			if petIndex ~= nil then
				if type(petIndex) ~= "boolean" then
					local str = "Pet" .. tostring(petIndex)
					if hatchFrame:FindFirstChild(str) ~= nil then
						local frame = hatchFrame[str]
						local rName,typ = getPetName(petName)
						local rarity = getRarity(rName)
						frame.Rarity.Text = rarity
						frame.PetName.Text = tostring(petName)
						if typ == "Shiny" then
							frame.PetName.TextColor3 = Color3.new(1, 1, 0)
						else
							frame.PetName.TextColor3 = Color3.new(1, 1, 1)
						end
						local rarityColor = rarityColors:FindFirstChild(tostring(rarity)).Display
						frame.Rarity.TextColor3 = rarityColor.Value
					end
				end
			end
		end
	end
	setHatchFrameTexts()
	
	-- clone egg
	local function createEgg(petIndex)
		if egg[eggName..petIndex] == nil then
			--print("New egg cloning start...")
			local directory = rep:WaitForChild("Extra").Eggs
			egg[eggName..petIndex] = (directory:FindFirstChild(eggName) or directory["Default Egg"]):Clone()
			egg[eggName..petIndex].Name = "Egg"
			egg[eggName..petIndex].Parent = particleFol
			for a, b in pairs(egg[eggName..petIndex]:GetDescendants()) do
				if b:IsA("BasePart") then 
					b.CanCollide = false 
				end
			end
		else
			-- make egg disapear if the egg already in the cache
			--print("Egg already in cache, makes it appear...")
			for a, b in pairs(egg[eggName..petIndex].Egg:GetDescendants()) do
				if b:IsA("BasePart") then
					b.Transparency = 0
				end
			end
		end
	end
	createEgg(petIndex)
	
	-- lock Camera
	--player.Character.Humanoid.WalkSpeed = 0
	--player.Character.Humanoid.JumpPower = 0
	camera.CameraType = "Scriptable"
	player.CameraMinZoomDistance = 6
	
	-- render step to display egg
	local con = rs.Heartbeat:Connect(function(dt)
		egg[eggName..petIndex]:SetPrimaryPartCFrame(camera.CFrame * offset * pos.Value)
	end)
	pos.Value = CFrame.new(0, 3, 0)
	
	-- Move Egg Up
	pcall(function()
		playTween_Func(pos,{1, "Bounce", "Out", {Value = CFrame.new()},})
		--playTween_Func(pos,{(1 / speedDiv), "Bounce", "Out", {Value = CFrame.new()},})
	end)
	
	-- Play Woosh sound
	if not petIndex or petIndex == 1 then
		delay(0.25 / speedDiv, function()
			SoundService:Play({Name = "Whoosh",Parent = egg[eggName..petIndex],Volume = 0.5})
		end)
		delay(0.6 / speedDiv, function()
			SoundService:Play({Name = "Whoosh",Parent = egg[eggName..petIndex],Volume = 0.5})
		end)
	end
	wait(0.75 / speedDiv)
	
	-- Rotate Egg
	local function rotateEgg()
		local num = 0
		local len = 0.25
		local size = 30
		repeat
			wait(len)
			num = num + 1
			if num == 3 then
				num = 1
			end
			local value = CFrame.Angles(0, 0, math.rad(size * (num == 1 and 1 or -1)))
			pcall(function()
				playTween_Func(pos, {len, "Sine", "Out", {Value = value}})
			end)
			len = len / 1.15 / (speedDiv > 1 and speedDiv / 1.5 or speedDiv)
			size = size / 1.025
			if not petIndex or petIndex == 1 then
				SoundService:Play({Name = "EggPop",Parent = egg[eggName..petIndex],Volume = 0.75})
			end
		until len <= 0.015
	end
	rotateEgg()
	
	-- Create Pet
	local function createPet(petIndex)
		if pet[petName..petIndex] == nil then
			--print("Pet is new, start cloning...")
			local directory = rep:WaitForChild("Pets")
			local rName,typ = getPetName(petName)
			pet[petName..petIndex] = directory[rName]:Clone()
			for a, b in pairs(pet[petName..petIndex]:GetDescendants()) do
				if b:IsA("BasePart") then
					b.Anchored = true
				end
			end
			setupPetModel(pet[petName..petIndex], typ)
			for a, b in pairs(pet[petName..petIndex]:GetDescendants()) do
				if b:IsA("Trail") then b.Enabled = false end
			end
			pet[petName..petIndex].Parent = particleFol
			pet[petName..petIndex]:SetPrimaryPartCFrame(camera.CFrame * offset)
		else
			-- Pet already exist in cache
		end
	end
	createPet(petIndex)
	
	-- move egg out
	pcall(function()
		playTween_Func(pos, {0.01, "Sine", "Out", {Value = CFrame.new()}})
	end)
	
	-- render pet
	local newOffset = offset + Vector3.new(0,1,0)
	local extraCframe = (function()
		if petIndex ~= nil and type(petIndex) ~= "boolean" then
			return CFrame.new(-1 * (tonumber(petIndex) - 2), -1, 1.5)
		end
		return CFrame.new(0, -1, 1.5)
	end)()
	local petRender = rs.Heartbeat:Connect(function(dt)
		local cframe = camera.CFrame * pos.Value * newOffset * CFrame.new(petIndex and -1 * (petIndex - 2) or 0, -1, 1.5) * CFrame.Angles(math.rad(-10), math.rad(0), 0)
		pet[petName..petIndex]:SetPrimaryPartCFrame(cframe)
	end)
	
	-- make egg disapear
	egg[eggName..petIndex]:SetPrimaryPartCFrame(CFrame.new(0,0,0))
	--for a, b in pairs(egg[eggName..petIndex].Egg:GetDescendants()) do
		--if b:IsA("BasePart") then
			--b.Transparency = 1
			--b:ClearAllChildren()
		--end
	--end
	con:Disconnect()
	--egg:Destroy()
	--egg = nil --added Azizi
	
	-- egg pops 
	SoundService:Play({Name = "Poof2",Parent = pet[petName..petIndex],Volume = 0.75})
	SoundService:Play({Name = "EndEgg", Parent = pet[petName..petIndex], Volume = 0.5, Time = 3.9})
	
	-- if legendary + pet, sounds = particles appear
	local rName = getPetName(petName)
	local rarity = getRarity(rName)
	if rarity == "Legendary" or rarity == "Mythic" or rarity == "Mystery" then
		SoundService:Play({Name = "Special_Unlock",Parent = pet[petName..petIndex],Volume = 1})
		if IsEffectsEnabled() == true then
			for a, b in pairs(legendaryEffects:GetChildren()) do
				if b:IsA("ParticleEmitter") then
					local particle = b:Clone()
					particle.Enabled = false
					particle.Parent = pet[petName..petIndex].PrimaryPart
					particle:Emit(legendaryEffects.AmountToEmit.Value)	
				end
			end
		end
	end
	
	-- New pet to the index, set ui and sounds
	local function roundNumber(num, numDecimalPlaces)
		return tonumber(string.format("%." .. (numDecimalPlaces or 0) .. "f", num))
	end

	if newPet then
		do
			local frame = not petIndex and hatchFrame.Pet or hatchFrame["Pet" .. petIndex]
			if frame then
				delay(0.5 / speedDiv, function()
					SoundService:Play({Name = "NewSpecies",Parent = pet[petName..petIndex],Volume = 0.5})
					frame.Discovered.Text = "New Pet Discovered!"
					frame.Discovered.TextTransparency = 0
					frame.Discovered.TextStrokeTransparency = 0
					
					-- Added by Azizi 1 Feb 2022 for Egg Progress Bar
					local eggTotal = indexUI.ProgressBar.eggTotal.Value
					local eggProgress = indexUI.ProgressBar.eggProgress.Value
					eggProgress = eggProgress + 1
					local percentageProgress = (eggProgress / eggTotal) * 100
					local sizePercentage = percentageProgress/100 * 0.842 --0.842 is the 100% progress size
					indexUI.ProgressBar.progress.Text = ""..eggProgress.."/"..eggTotal.." ("..tostring(roundNumber(percentageProgress,2)).."%)"
					indexUI.ProgressBarBack.Size = UDim2.new(0.842-sizePercentage, 0, 0.074, 0);
					indexUI.ProgressBar.eggTotal.Value = eggTotal
					indexUI.ProgressBar.eggProgress.Value = eggProgress
					
					player.PlayerInfo.TotalEggProgress.Value = eggProgress
					
					--game.ReplicatedStorage["EggProgress"]:FireServer(eggProgress, eggTotal)
				end)
			end
		end
	end
	
	-- make pet frames appear
	delay(0.15 / speedDiv, function()
		local frame = not petIndex and hatchFrame.Pet or hatchFrame["Pet" .. petIndex]
		if frame then
			frame.PetName.TextTransparency = 0
			frame.PetName.TextStrokeTransparency = 0.75
			frame.Rarity.TextTransparency = 0
			frame.Rarity.TextStrokeTransparency = 0.75
		end
	end)
	
	-- ending function
	local skipped = false
	local function ending()
		luckComboTextUI.Visible = false
		skipped = true
		
		-- fade out things in frame
		local frame = not petIndex and hatchFrame:FindFirstChild("Pet") or hatchFrame:FindFirstChild("Pet" .. petIndex)
		if frame then
			hatchFrame.Skip.Visible = false
			pcall(function()
				playTween_Func(pos, {1, "Linear", "Out", {Value = CFrame.new(0, -10, 0)}})
				--playTween_Func(pos, {(1 / speedDiv), "Linear", "Out", {Value = CFrame.new(0, -10, 0)}})
			end)
			if frame:FindFirstChild("PetName") ~= nil then
				frame.PetName.TextTransparency = 1
				frame.PetName.TextStrokeTransparency = 1
			end
			if frame:FindFirstChild("Rarity") ~= nil then
				frame.Rarity.TextTransparency = 1
				frame.Rarity.TextStrokeTransparency = 1
			end
			if frame:FindFirstChild("Discovered") ~= nil then
				frame.Discovered.TextTransparency = 1
				frame.Discovered.TextStrokeTransparency = 1
			end
		end
		
		turnOnOffOtherUI(true) turnOnOffEggBuyFrame(true, eggName) gameInterfaceUI(true) turnOnOffPetNameTags(player, true)
		
		-- unlock camera
		--player.Character.Humanoid.WalkSpeed = setPlrSpeed(player)
		--player.Character.Humanoid.JumpPower = 50
		camera.CameraSubject = player.Character
		camera.CameraType = Enum.CameraType.Custom
		player.CameraMinZoomDistance = 0.5
		
		wait(1 / speedDiv)
		
		--pos:Destroy()
		--pet:Destroy()
		pet[petName..petIndex]:SetPrimaryPartCFrame(CFrame.new(0,0,0))
		--pos = nil  --added Azizi
		--pet = nil  --added Azizi
		hatchFrame:Destroy()
		hatchFrame = nil
		petRender:Disconnect()
	end
	
	-- make skip button appear
	delay(1 / speedDiv, function()
		hatchFrame.Skip.Visible = true
		wait(0.75 / speedDiv)
		if auto == true then
			ending()
		end
	end)
	
	-- skip button click func
	pcall(function()
		hatchFrame.Skip.MouseButton1Down:Connect(function()
			SoundService:Play({Name = "Click",Parent = script,Volume = 0.5})
			ending()
		end)
	end)
	
	-- End if skip button was not pressed
	delay(3 / speedDiv, function()
		if skipped == true then
			return
		end
		ending()
	end)
end

function EggService:MultiHatchEgg(eggName, petData)
	local offset = CFrame.new(0, 0, -3.5) * CFrame.Angles(0, math.rad(180), 0)
	for i = 1, 3 do
		do
			local num = i - 2
			spawn(function()
				local petName,newPet,shiny = petData[i][1],petData[i][2],petData[i][3]
				self:HatchEgg(eggName, petName, newPet, offset + Vector3.new(2.5 * num, 0, 0), tonumber(i), shiny, false)
			end)
		end
	end
end

return EggService

My code clone the actual egg model and display it infront of the player… previously what i had done is that there was local script and keep on cloning and then destroy and disconnect some variables. But now i have ammend that and make if the egg already had hatched before by the player, DONT clone but instead use the previous declared cloned instance to reduce the memory usage of keep cloning… the code as above… BUT still the memory usage is keep getting increase over time while egg hatching… what is wrong with my code and how should i improve it to lower the memory usage like other game. Some game people can grind egg for days without leaving the game…

Thanks in advance

It’s a memory leak. Word of wisdom, don’t just post your entire script, let alone a very lengthy one, onto the DevForum and expect someone to magically fix it. I don’t think anyone would have that much free time to read all of it anyways. Not to mention, you’re making your game vulnerable to exploiters by literally tossing its source code out into the open. You should try to pinpoint the issue yourself by isolating each snippet and testing its behavior.

There are plenty of resources out there on how to deal with memory leaks. You should be able to find them with a bit of searching.

1 Like

My best guess is you have a connection that is not being disconnected, I believe all connections related to that object get disconnected when that object is destroyed though I’m not sure how reliable that is. Personally, I would disconnect it myself and see if that lowers the memory usage. I believe it’s the skip button, I would just disconnect it when it’s not in use and connect it when it is. Though I highly doubt it’s just his one connection causing it. Like @Prototrode have a look around I would start by disabling this script and stop it from running and see how your memory usage is then if it doesn’t change then it’s somewhere else in your game, also use the better practices like using task.wait() instead of wait() and task.spawn() instead of spawn().

I have been researching for over 2 month now… trying all the possible way mentioned in the forum…

disconnect any connection
destroyed unsed cloned model
get the unused variable to nil

yet all those still not the answer to my memory leak issue… i still have trouble finding the solid possible answer… thats why i posted a whole code there… because on my previous post… i want to PAY a scripter and someone says it is not permitted to do so… because i know good scripter dont have time unless i PAY for their time to help me fix this…

Holy moly that is a lot of code, how much is the memory leak btw? Also use print statements to try and identify the problem because tbh I ain’t reading that.

1 Like