Tables not properly being garbage collected

Code that previously properly cleaned up after itself may no longer do so, I’ve tested many times with different pieces of code and usually they may not properly garbage collect.

The 1st Repro is the easiest to reproduce and will always memory leak, regardless of the table size, deleting the script that created the table, de-referencing the table, waiting before deleting the script that referenced the table, and so on. The place is a default base-plate with nothing intensive that runs, even the default chat is disabled, and even has player character appearance turned off.

1st Repro Game: Memory Leaker Tester - Roblox (Uncopylocked, you can edit it in Roblox Studio)
1st Repro Code: (Acts the same in non-Parallel as well, won’t garbage collect the table, I’ve also tested having a smaller table and having a larger table the size of the table doesn’t matter and will never garbage collect even if the table will end up crashing the player)


1st Repro Picture: (I even waited 15 minutes after clearing the table & deleting the script for it to potentially garbage collect the 2 GBs of memory usage from the table)
(Client-Side)
(Server-Side)
(Server-Side memory usage)

2nd Repro Code: (Not the same as above, this code should almost always garbage collect)

local Table: {string} = {"Testing GC"}
local GCCheck = setmetatable({Table}, {__mode = "kv"})
Table = nil
task.wait(10)
print(GCCheck[1])

2nd Repro Picture:
image

For some reason, in Roblox Studio the 2nd repro will randomly either garbage collect or not, it seems to be a random chance for the table to garbage collect, however, the 1st repro will always memory leak regardless of play testing in Roblox Studio or joining the game in the Roblox Player.

I haven’t tested any other forms of memory leaking that doesn’t include tables so I’m unsure if other things that should be garbage collecting aren’t being done. This hasn’t always been the case as tables use to properly garbage collect always, I’m unsure of when this first started happening though so I presume that someone changed how the garbage collector works.

System Information: (Shouldn’t be necessary as I’ve had multiple players join the 1st repro game and have it happen to them as well)
Windows 11, Intel i9-13900KF, 64GBs DDR5, NVIDIA 4090

Expected behavior

The tables in both example should always clear the table and garbage collect it, especially if the table is using up to 2 GBs of memory usage and holds zero references and the script it originates from was deleted after clearing the table and its references. In actual games there’s a few cases where this memory leak is happening as it’s how I found out about it. (Of course the actual game wasn’t using a 2GB table but after awhile it starts to add up from never garbage collecting)

14 Likes

I’m seeing a significant number of servers with an age <12 hours with extremely increased memory counts in my game due to this issue.

A 7 hour old server:

A 25 hour old server:

Both of these servers are running the same game version and are identical.

The performance analytics also show a (very) large increase in memory:
image

I’ve run @Crab_Wrangler’s test script in various servers and found that the ones with the memory issues correlate to the ones with this issue.

1 Like

You are seeing ‘UntrackedMemory’ increase, the original topic is about ‘LuaHeap’, you should report your issue separately.

Hi WheretIB, thanks for responding to the issue so quickly,

I confirmed last night with the post’s creator that it can appear under untracked memory instead of luaheap (presumably under different conditions such as the script being destroyed)

If you still believe it to be a separate issue please let me know so I can make a separate bug report.
Thanks

Heyo, here’s some pictures to corroborate that this can cause the section, “UntrackedMemory”, to increase visually in memory usage as well: (So it’s not just LuaHeap that increases in memory usage)


(Tested in a game that doesn’t even load in player characters and only reproduces this specific table GC bug)

1 Like

If you have this problem on live servers, please mention the link to the game.

Game Link: Memory Leak - Roblox (You have no idea how long it took for me to make the place public, as well as, publish the place, with the constant 500/502 internal server errors from the recent outage :sob: ) (Uncopylocked, the original repro in the previous post works as well and is still uncopylocked)

Picture: (The highlight green mark is before the 15s, so before the scripts start memory leaking, with this test it raises about 25/50 MBs, which sure isn’t much but should be enough proof for the theory)

I noticed the UntrackedMemory issue becomes more prevalent with the more memory usage you take up with the table, so if you increase the amount of indices the table has it’ll be more obvious but will make the game use a ton more memory usage. While this may not be a real world example of the issue, I’m not 100% sure on how to create a more reliable reproduction that doesn’t just eat memory to reproduce the issue with the memory leak. Since verret notices it a lot more, I expect it may be the information he’s storing within tables or how he’s using the tables, something specific within his game. As a possible reason, in all of my testing, and my experiences, I don’t usually use tables a lot within my module scripts while verret says he uses a lot of modular code with ModuleScripts which could be exacerbating the issue and causing the UntrackedMemory.

To make it more obvious, I yield the script that adds to the tables for 15 seconds (You can end it early by pressing E), and then have all the scripts start up at the same time after that so you can compare the before and after. You can test this in a live game, and it should work on the same in a server script, as well as, in non-Parallel Lua.

15s delay:


Source of the script leaking memory usage: (The underline is from type checking stating converting a table to nil)
image

2 Likes

There is a misunderstanding on how Luau (and Lua and other garbage-collected languages) work with memory.
Luau uses garbage collection that is a task separate from the running scripts.
There are no reference counting and things like table.clear or t = nil do not free memory.
Memory that is no longer referenced is collected after a while (with one caveat for Parallel Luau threads) and this process is not deterministic.

In your ‘Memory Leaker Tester’ example with Parallel Luau we have identified an issue in garbage collection being paused because there are 0 parallel tasks running after the allocation is completed. We will have a fix for this issue.

We were not able to reproduce this issue, all memory is collected when Parallel Luau is removed from example and ‘Leaker’ scripts run in serial.

Weak table keys are garbage collected eventually. When there is no memory pressure, it might take a long time.

There were no changes related to garbage collection in Luau since July 2022.

Sorry, what I meant was a link to ‘actual games’ that you mentioned.
But still, thank you for example, we have looked if there are any issues there.

For ‘UntrackedMemory’ increasing by Luau it’s just memory reservations made by operating system. For example, when table is increased from 1MB to 2MB, 3MB total are used for a short time and OS keeps the 1MB around for future requests. These are not leaks and will be reused for other allocations.

1 Like

Yes, you should still create a separate issue as this large increase in ‘UntrackedMemory’ in fresh servers is unrelated to Luau, even considering the Parallel Luau GC issue described above.

Thanks for looking in-depth into this issue, I appreciate it. Also I believe verret’s problem was actually from this thread, which was fixed and he said his servers started behaving normally: Server network/raknet Memory Leak? - #5 by PlumJar . Also thanks for the description on how UntrackedMemory works.

We were not able to reproduce this issue, all memory is collected when Parallel Luau is removed from example and ‘Leaker’ scripts run in serial.

In the first repro game provided (Memory Leaker Tester - Roblox) there’s a LocalScript within ReplicatedFirst which does not have any actors and does not run in Parallel Luau, it recreates the issue where the table is never garbage collected. The memory usage for LuaHeap will also permanently increase afterwards. I’ve went ahead and enabled the LocalScript which reproduces the issue in non-Parallel Luau, and disabled the LocalScript that runs in Parallel Luau and put it in ServerStorage.


Six minutes in and the memory hasn’t cleared and it’s been climbing for some reason:

1 Like

Thank you for additional description.
I have tested this scenario as well and memory is collected eventually, which means there are no live references remaining to hold the data (which is what we expect here).
It does take a lot of time time to collect, which is related to the memory size that was used and the fact that empty baseplate doesn’t have too much going on to allocate new memory.

1 Like

Okay, thanks for the in-depth descriptions on multiple queries I’ve made in this topic, is there an ETA for when the Parallel Luau bug will be fixed?

Also, for the LuaHeap thing, is there a reason that it increases quite a lot over time even after the script that was collecting all that memory stopped running? Also is there any way to force the garbage collector to run on large complex calculations that would run similar to something like that script after finishing, as an example, an experience I was making for a bit would save a lot of frames to a table for each pixel on the screen to save the color of what was on the screen in that area for displaying a pixelated version on the screen, however, after finishing how would I go about making it forcefully garbage collect so the client just isn’t holding onto all that memory? I suspect when I would do something intensive it would allocate the memory to the new task but what if either I’m no longer planning to do intensive work after the fact or I would just prefer not having the client display it’s using 1+ GBs after the task has been finished (and Roblox using that much memory in the background while the user could be multi-tasking), would it be better to just make an engine feature request for something like this?

Thanks for explaining about LuaHeap and that what I found wasn’t actually a bug, and that the engine will clear it from memory when allocated.

1 Like

A fix is prepared, but I can’t tell you when the next update releases

In serial mode there are game scripts running that are written by Roblox, character controller, animator, camera, etc.

No, there is no API.

Maybe, but I’m not sure how popular your request will be, I don’t remember this issue coming up before.

The issue with Parallel Luau memory not being collected when parallel scripts are not running have been fixed.

2 Likes

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