Incredible memory usage and performance issues with many parts

yeah… im gonna be honest - i have absolutely no clue what to do to fix it
i have a local function in a local script that creates a new part, stores it in local variables, does a bunch of stuff to it and then removes the part after a couple seconds

the memory usage of the script increases every isngle time, to the point of weighing 3mb for no reason after ~5k calls
^^ not sure if related but i get like 40 fps after said 5k calls, and that only gets worse and worse with each and every single execution

so im looking for a way to fix the fps issues and preferably everything else

local function Animate(Piano, PianoNote, isHolding)
    local Piano_Keys = Piano.Keys
    if PianoNote then
        local PianoKey = Piano_Keys:FindFirstChild(tostring(PianoNote))

        if PianoKey then
            
            local part
            local isPartActive
            
            if animholder:FindFirstChild(PianoNote) and not isHolding then
                isPartActive = animholder[PianoNote].Holding
            else
                part = (IsBlack(PianoNote) and blacknote or whitenote):Clone()
                part.Size = Vector3.new(PianoKey.Size.X / 2, 0.1, PianoKey.Size.X / 2)
                part.Name = PianoNote
                part.Position = Vector3.new(PianoKey.Position.X, PianoKey.Position.Y, PianoKey.Position.Z - (PianoKey.Size.Z / 2))
                part.Orientation = Vector3.new(0, PianoKey.Orientation.Y, 0)
                part.Anchored = true
                part.Transparency = 0
                part.Parent = animholder

                local temp = Instance.new("BoolValue")
                temp.Name = "Holding"
                temp.Parent = part
                isPartActive = temp
            end

            isPartActive.Value = true

            if isHolding then
                task.delay(heldtimeout, function()
                    if part and part:IsDescendantOf(animholder) then
                        isPartActive.Parent.Name = "past_" .. tostring(PianoNote)
                        isPartActive.Value = false
                        task.delay(deletiontimeout, function()
                            if part and part:IsDescendantOf(animholder) then
                                part:Destroy()
                            end
                        end)
                    end
                end)
            end

            task.spawn(function()
                while isPartActive.Value do
                    if isHolding then
                        part.Position = part.Position + Vector3.new(0, scrollspeed / 2, 0)
                        part.Size = part.Size + Vector3.new(0, scrollspeed, 0)
                    else
                        part.Position = part.Position + Vector3.new(0, scrollspeed, 0)
                    end
                    task.wait() 
                end
            end)

            if not isHolding then
                task.spawn(function()
                    isPartActive.Parent.Name = "past_" .. tostring(PianoNote)
                    isPartActive.Value = false
                    task.delay(deletiontimeout, function()
                        if part and part:IsDescendantOf(animholder) then
                            part:Destroy()
                        end
                    end)
                end)
            end
        end
    end

    local connection
    connection = Piano.SeatOccupant.Changed:Connect(function(val)
        if val == nil then
            connection:Disconnect()
            for _, v in animholder:GetChildren() do
                v.Holding.Value = false
                task.delay(deletiontimeout, function()
                    if v:IsDescendantOf(animholder) then
                        v:Destroy()
                    end
                end)
            end
        end
    end)
end

um something seems fundamentally wrong in addition to what im saying below, can you describe what you’re trying to make (like, actually trying to make, ex: piano keys move up & down with held input)

creating new parts can get costly, you should consider looking into object pooling, people have already made modules/systems to do this but i cant vouch on whether or not they’re good

looks like a memory leak here so long as a player remains seated

local connection
connection = Piano.SeatOccupant.Changed:Connect(function(val)
--etc...

basically i send a remote to a client which then visualizes piano keys by making new parts that float upwards (kinda like a rousseau video - https://www.youtube.com/watch?v=Zj_psrTUW_w)

said parts get deleted within 10 seconds max so i dont think that should be a problem? (unless part:Destroy() somehow keeps the parts somewhere)

or could it be that the script keeps the part in memory even after they are deleted and all code in the function has finished running?

i dont think that should be a problem?

yeah probably not, it’s a time complexity optimization & not a space complexity one which i’m now realizing isn’t what you need

the written logic is hard for me to think about so it’d probably be easier to run some tests (prints) on potentially problematic spots:

  • Piano.SeatOccupant.Changed never fires if the player never exits the seat, leading to its connection duplicating & staying for every subsequent call of Animate() (5k calls = 5k connections)
  • while isPartActive.Value do | potentially an infinite loop, test to make sure this is getting closed.

and unless some part (probably in animholder) isn’t being destroyed/is being kept in memory, those are the only potentially-issue spots that i see

i changed something inside of the seatoccupant.changed function sooooo profit i guess? it works okay now… thanks for the help i guess???

this is so weird i did nothing to fix it yet it fixed itself but eh if it works it works

1 Like