I’m attempting to diagnose and prevent memory leaks in a game I’m aiding development with.
I understand that variables and connections can leak memory instances and thus must be dealt with appropriately before the garbage collector can free the memory they occupy.
However, I’ve run into an impasse, and require further guidance.
While testing exactly what causes memory leaks in our game, I’ve found a possible culprit. These training dummies are never garbage collected once they are killed and destroyed. They cause the Instances, PhysicsParts, and Gui entries of the server memory to gradually and irreversibly rise, and I attempted my best at making sure they were properly garbage-collected, to no avail.
I removed all but one script essential to their operation in order to ascertain what behaviors I need to adopt in order to ensure no leaks occur, and then proceeded to follow the instructions of the many forums I’d read as to what needs to be done to ensure no references are left behind that could cause the collector to ignore the dead dummy. However, my attempt was not successful.
function wfc(p, o, t)
return p:WaitForChild(o, t)
end
function newC3(r, g, b)
return Color3.new(r/255, g/255, b/255)
end
--
local v3 = Vector3.new
local c3 = Color3.new
local ud2 = UDim2.new
--
local cs = game:GetService("CollectionService")
local ss = game:GetService("ServerStorage")
local rs = game:GetService("ReplicatedStorage")
local remotes = wfc(rs, "Remotes")
local spoof = wfc(remotes, "EffectSpoof")
local dpopup = wfc(remotes, "DmgPopup")
local cModules = wfc(rs, "Modules")
local common = wfc(cModules, "CommonEffects")
local sModules = wfc(ss, "Modules")
local core = require(wfc(sModules, "Core"))
--
local overhead = wfc(rs.Assets, "HealthMeter"):Clone()
overhead.Parent = script
local o_name = wfc(overhead, "NamePlate")
local o_bar = wfc(overhead, "Bar")
local o_hp = wfc(o_bar, "HP")
local o_lay = wfc(o_bar, "Underlay")
local o_sh = wfc(o_bar, "Shield")
local o_c = wfc(o_bar, "Counter")
local char = script.Parent
local hume = wfc(char, "Humanoid")
local hrp = wfc(char, "HumanoidRootPart")
local head = wfc(char, "Head")
local sf = wfc(char, "Stats")
local hp = wfc(sf, "HP")
local mhp = wfc(sf, "MaxHP")
local shd = wfc(sf, "Shield")
local spd = wfc(sf, "Speed")
local rec = wfc(sf, "Recovery")
--
local dead = false
local cons = {}
local defwait = 4
local regwait = defwait
local maxwait = 20
local old = hrp.Position
local new = char:Clone()
local lasthit = tick()
local chp = hp.Value
local cmhp = mhp.Value
local dsp = hume.WalkSpeed
local mreg = 4
--
local function getShields(char)
local num = shd.Value
local sf = char:FindFirstChildWhichIsA("Folder")
if sf and sf.Name:lower() == "stats" then
for _,v in pairs(sf:GetChildren()) do
if v.Name:lower() == "tempshield" then
num = num + v.Value
end
end
end
return num
end
local function hpsync()
local s = getShields(char)
local val = hp.Value
o_hp.Size = ud2(val/mhp.Value, 0, 1, 0); o_c.Text = val + s
o_sh.Size = ud2(math.min(s/mhp.Value, 1), 0, 1, 0)
if val <= mhp.Value * .25 then o_hp.BackgroundColor3 = newC3(255, 0, 0)
elseif val <= mhp.Value *.6 then o_hp.BackgroundColor3 = newC3(255, 255, 0)
else o_hp.BackgroundColor3 = newC3(50, 255, 50)
end
spawn(function()
while (tick()-lasthit) < .6 do wait() end
o_lay:TweenSize(o_hp.Size, "Out", "Quad", 0.4, true)
end)
end
--
table.insert(cons, hume.Died:connect(function()
hp.Value = 0; dead = true
end))
table.insert(cons, hp.Changed:connect(function(amt)
local diff = chp - amt
if amt <= 0 then dead = true; char:BreakJoints()
delay(5, function()
new.Parent = char.Parent
new:MoveTo(old); new = nil
char:Destroy()
end)
else if diff > 0 then
lasthit = tick(); regwait = math.min(regwait + (diff/10), maxwait)
end
end
chp = amt; hpsync()
end))
table.insert(cons, mhp.Changed:connect(function(amt)
if amt < hp.Value then hp.Value = amt
elseif amt >= hp.Value and hp.Value == cmhp then hp.Value = amt
end cmhp = amt
end))
table.insert(cons, spd.Changed:connect(function(val) hume.WalkSpeed = dsp * (val/100) end))
table.insert(cons, shd.Changed:connect(hpsync))
table.insert(cons, sf.ChildAdded:connect(hpsync))
table.insert(cons, hume.Touched:connect(function(part)
if cs:HasTag(part, "Void") or cs:HasTag(part, "Water") then
hp.Value = 0; shd.Value = 0
end
end))
--
overhead.NamePlate.Text = char.Name
overhead.Parent = head
hpsync()
--
while char.Parent do
if (hrp.Position - old).magnitude >= 500 then hp.Value = 0 end
if not dead then local now = tick()
repeat wait() until (tick()-now) >= .75
if (tick() - lasthit) >= regwait and hp.Value < mhp.Value then
local amt = math.ceil(mreg * (1.1 - (hp.Value/mhp.Value)))
amt = math.clamp(amt, 2, mreg)
amt = math.ceil(amt * (rec.Value/100))
hp.Value = math.min(hp.Value + amt, mhp.Value)
core:makePopup(head.Position + v3(0, 2, 0), "+"..amt, newC3(0, 255, 0), c3(), 1.2)
core:effect("heal", {part = hrp, power = amt * 2}, common)
regwait = defwait
end
else break
end
end
for _,v in pairs(cons) do v:Disconnect() end
cons = nil
If anyone could peruse that code snippet and determine exactly what’s causing the leak, I’d appreciate your help. Thanks for reading.