Instances lead to Increased Memory and Server Crashes

Bug Description:
When creating more Instances (for this report it’ll be a Part but can work for other Instances) whether by Instance.new() or :Clone(), the game’s memory does not decrease after the Instance has :Destroy() ran on it meaning the server is still keeping track of it. What is odd is the memory only increases after a higher amount of Instances that has been created in the past (the example is provided as a script).

Where it’s happening:
This bug is happening on a game’s server hosted by Roblox and not when emulating a server from Studios. The GarbageCollector in an emulated server appears to usually clear out the unused memory generated by the blocks, but not all of it.

How to replicate:

  1. Create an empty baseplate so there are no other scripts
  2. Join it by clicking the play button from the website
  3. Start creating/deleting parts from console and watch the memory skyrocket (example script provided)
    When placing the script provided in a server script, it still produces the same results.

Example script:

local AmountCreating = 100
--Have AmountCreating be the same number twice, and the memory only increases the first time.
--Increasing the number results in memory further increasing.
local ItemHolder = Instance.new("Model")
ItemHolder.Name = "ItemHolder"
ItemHolder.Parent = game.Workspace
for i=1,AmountCreating,1 do
    local Part = Instance.new("Part")
    Part.BrickColor = BrickColor.Random() -- I like rainbows
    Part.Parent = ItemHolder
    wait()
end
wait(10)
game.Workspace.ItemHolder:Destroy() --Memory doesn't decrease even when this is done

This is only 100 parts. Have you tested if the same happens if you constantly do this / with a higher amount of objects?

It might be that the memory occupied by 100 parts is not considered significant enough by the engine to clean up. You might have to hit a certain threshold of memory before it considers freeing up the old memory.

I’m double checking this now, but in theory it should still behave the same. I’m also making the parts anchored so the game doesn’t take into account the physics of the part and solely the part’s existence.

I just did a huge server stress test with a script that generates about 700MB of memory (higher than what most games usually have), and can confirm that the memory is still not decreasing even after destroying ItemHolder. All of the “unused” memory gets transferred into UntrackedMemory.

Script:

local AmountCreating = 100000
local ItemHolder = Instance.new("Model")
ItemHolder.Name = "ItemHolder"
ItemHolder.Parent = game.Workspace
for i=1,AmountCreating,1 do
    local Part = Instance.new("Part")
    Part.BrickColor = BrickColor.Random() -- I like rainbows
    Part.Anchored = true --We're not caring about physics calculations
    Part.Shape = "Ball"
    Part.CFrame = CFrame.new(math.random(-5000,5000),0,math.random(-5000,5000)) --Somewhat prevents render lag
    Instance.new("Fire",Part) --This is to create extra "garbage" and yes, ik you shouldn't do this
    Part.Parent = ItemHolder
end
print("done")
wait(10)

game.Workspace.ItemHolder:Destroy() --Memory doesn't decrease even when this is done
--Even waiting a few minutes, the server's memory doesn't decrease

Possibly relevant:
https://devforum.roblox.com/t/why-is-luaheap-memory-from-destroyed-instances-not-being-freed-up/447767

The issue from that post is similar to what’s happening when emulating a server from Studios yet it’s not what’s happening with Roblox’s servers themselves. That is lua scripts tracking Instances, not the game itself. The response quoted does somewhat relate to this, and it’s probably something to do with garbage collection.

Our allocators internally may not release memory even when the script frees something. For one thing, if you free one object, it might have been allocated from the OS in a large block, and we can’t release the block back to the OS until everything in the block is freed. But even if everything in the block is freed, it’s often more efficient to just keep the block under the assumption that the memory will be needed again.

It would be a leak if you allocated all of the objects, freed them, allocated them, and then memory usage went up. In that case, it would be clear that the memory wasn’t being freed even inside our own allocator.

Very interesting insight on how the game’s memory works, and I’m sure many would also love knowing about this; thanks for the response!

I’ll mark this solved since I noticed my larger games that create/destroy tons of instances are no longer crashing after an extended amount of time. I’m guessing/hoping something was fixed.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.