How to avoid high UntrackedMemory?

We have a mining game that absolutely must deal with thousands upon thousands of parts, while hundreds are constantly being created and destroyed due to cave generation and miners. The problem with this is that when the mine resets, UntrackedMemory skyrockets on the server side and never dips. Could this have something to do with tables? We have no idea what to look for when trying to solve this. There are no other memory problems in the game, all other memory goes up as the mine gets bigger but goes back to normal once the mine resets. All of that memory transfers to untracked.
Is there anything specific that could be causing this, or does this come with deleting large amounts of objects?

1 Like

From this post, a lot of people said

“Having untracked memory high is not something developers can do much about”

Besides making your scripts more efficient, or finding a way to reduce memory exhaust, you can’t do much.

An option to reduce untracked memoy is calling garbagecollect(), which trashes uneeded data forever. But roblox doesn’t even let you do that, you can just do garbagecollect("count") which tells you how much memory is used by lua. Even if you called this, and got a rid of uneeded untracked memory, that’s still memory used by lua, and nothing related to the 3D world, so still some problems.

You did say that this comes from deleting a lot of object, this is correct! Because what happens is, when a piece of data is removed, it’s not fully removed, it’s still there in memory, which might pile up and create a lot of useless data.

For tables, you can use weaks tables, here is more info, allthough just like garbagecollect(), this won’t do much I think.

2 Likes

I want to debunk “UntrackedMemory climbing and not falling” as a myth because I’ve only experienced climbing UntrackedMemory with certain code that is bound to have memory leaks. I really don’t believe that this is an unsolvable issue out of our control.

Just my two cents by the way. I’m still looking into it and all.

2 Likes

I think you can do something about it. First off, when your mine resets, perhaps try adding everything onto debris service? I’ve heard rumors (probably nothing) that :Destroy() isn’t efficient. I’d imagine however that if you’re loading the mine at a certain point, and then destroying it, just loading every part with debris service when the mine resets and have debris service remove it upon the next mine refresh might fix a lot.
Debris Service has other advantages too, in which if the part no longer exists, it shouldn’t error (don’t quote me on that).

In the event that all of that is true, in theory you can reload your mine and get rid of a lot of what I assume to be tables tracking parts for you to use :Destroy() on, since debris service would just destroy it for you at the time you set.

Hope I was of some assistance.

1 Like

As of now, everything in the mine is stored in a giant table to make cave gen and block checks quick, and when a block is destroyed, it removes it from the table. I’ve read that untracked memory has to do with tables referencing dead objects, but I’ve also read debris causes problems with memory. I’m not sure the problem even has to do with the object destroying part, maybe something with references.

Debris almost definitely internally calls something like Destroy. Debris would be less efficient if anything.

3 Likes

Are you sure you’re removing it from the table or are you assuming it’s removed from the table?

I’m sure. The length of the table reads as less and less as the mine resets.

Welp just a reminder that using :Destroy() or DebrisService places a instance in nil and disconnects any script connections/signals with the instance but doesn’t necessarily remove them from tables. Make sure to look through your mine-reset more carefully, also potentially look at your generated ores/rocks to make sure they aren’t doing any unexpected behavior while resetting the cave/being reparented. It’s a untracked memory issue so it’s most likely dealing with either tables, connections, or overused signals.

1 Like

Would you mind sharing any code where you interact with your mine table during the reset process? I’m by no means an expert on garbage collection but I’ve learned enough that I may be able to see a memory leak if you’ve got one. It’s likely you have some kind of memory leak.

The actual block part is really short, all it does is delete the block and set the position of it in the coordinate table to nil.

Do you do this from one thread or some kind of event? If you do it within one thread (e.g. a while loop with waits) try setting the reference to nil as well. This should dereference the block as it may still be referenced since the thread never ends.

It’s done in a global function that is run by the script on a one-hour timer.

Is this in one thread though? Is your timer a while loop with waits which call the function of is it some kind of event system? If it’s in one thread you should try setting the reference to nil like I suggested since the thread needs to end to cleanup.

Which reference? All references in the function are set to nil at the end of each use.

Even variables? If so that would cross out my idea

I don’t know if there’s a way that you can avoid it. I just did a test and it’s kind of unsettling. UntrackedMemory was ~86 before running the test and shot to ~260, and never went back down. Here’s the code:

startButton.ClickDetector.MouseClick:Connect(function()
	for i = 1, 500 do
		local newPart = Instance.new("Part")
		newPart.Parent = workspace
		
		newPart = nil --the inclusion of this line has made no difference
	end
end)


stopButton.ClickDetector.MouseClick:Connect(function()
	for i,v in pairs(workspace:GetChildren()) do
		if v.Name == "Part" then
			v:Destroy()
		end
	end
end)

--method two; both have the same results

local parts = {}
startButton.ClickDetector.MouseClick:Connect(function()
	for i = 1, 500 do
		local newPart = Instance.new("Part")
		newPart.Parent = workspace
		
		table.insert(parts, newPart)
	end
end)


stopButton.ClickDetector.MouseClick:Connect(function()
	for i,v in pairs(parts) do
		v:Destroy()
		parts[i] = nil
	end
	parts = {}
end)

Even after clicking the stop button UntrackedMemory didn’t go back down.

2 Likes

Hmm, does wrapping things in event connections change the way it’s garbage collected or are there alternatives? After much testing with my problem, it really seems like there’s nothing wrong with tables. I’m thinking of trying other methods instead of tables because of the massive problems that it can cause.

I don’t know about how the event connections relate to garbage collection but I can tell you that you should make sure to disconnect connections that you don’t need anymore. I don’t think that tables are causing the rise in UntrackedMemory. I’ve noticed UntrackedMemory rising when destroying parts on the server for months now and I’ve tried a lot of different things to prevent it, but nothing works.

It seems to be a problem that’s affected a lot games, including top games, for a while now:

1 Like

Idk if this will help but if destroying blocks is a issue couldn’t people just make a folder in server storage that holds all the destroyed parts in a folder and if it’s for example a block that respawns every so often.

Couldn’t they just move that block back into a different area which would not use :Destroy() and not be going into Garbage collector. If it works it would more likely be a temporary fix.

Also I have a other idea that might work wich is same method but having a bunch of blocks that you need to fill up the mines into a folder and then your script could take from the folder in server storage and place them at appropriate spots and would defeat the purpose of using destroy but it Probobly won’t raise untracked memory because you use the same block each time you generate but you move them in diffirent spots every time you generate so you don’t use Destroy()

I haven’t tried this before but it could work.