Holy Mother of Memory Leaks

Memory leaks, memory leaks seemingly everywhere!
Or maybe it’s just the one source rearing its ugly head all over the place (oh please be the case). You know when you ignore a problem and hope it goes away but it doesn’t… then it instead gets worse? Perfect then you understand exactly how I got here.

I swear I’m not lazy; I’m just clueless
So, I’d like to clear up some memory leak(s), but I don’t know where it’s(they’re?) coming from. As a prerequisite just know that I did in fact look at community tutorials “PSA: Connections can memory leak Instances!” by Stravant. As well as “Garbage Collection and Memory Leaks in Roblox - What you should know” by Hexcede. I think they’re done fantastically, but unfortunately even with that knowledge it doesn’t seem to apply to the scripts, or perhaps I’m misunderstanding or maybe even missing something?

As I understand from those tutorials, what applies to the scripts is only Connections that don’t break. There are occasions where certain objects that don’t get duplicated, cloned, etc. that you interact with never get disconnected. Since I always want you to be able to interact with some buttons, it doesn’t make sense to me as to why I would disconnect them. Otherwise other connections are made for cloned objects, but they are later Destroyed.

Instead Now, I ask of you
With how the memory leak(s) present itself(themselves), could you help me figure out what kind of script would be the issue? I have for you, how the memory leaks are presenting themselves.

To Start:


as you can see in the PhysicsParts in the CLIENT SIDED Memory usage only continues to climb. “What causes this,” you ask? Well, strangely enough, resetting. Resetting your character will cause PhysicsParts memory to steadily rise.

This is the result of resetting around twenty times:


You can see from the first and second image CLIENT SIDED PhysicsParts went from around 100 to 320.

The large image with the graph and the smaller one without are both from the same session. Now what you’re telling yourself is probably along the lines of “Well that could be anything, how am I supposed to know,”

Aha! well another clue:
Instance+10k
The Circled number goes up by 10,000

This is also in the same session as the previous two, and I will also show a (low quality) video that demonstrates what I’m trying to show in this picture a little better. Now unlike the previous two images I don’t really know what this means. Since, in a baseplate that number is approximately 181,000, so I assume it doesn’t actually indicate the number of instances. This one may be irrelevant information due to me not understanding it, apologies.

Perhaps in a different vein, there is also another issue

Third:


The CLIENT SIDED LuaHeap memory usage steadily increases.

After about half an hour the LuaHeap steadily climbs from 300 to 400. Unlike the PhysicsParts this does not appear to be influnced by anything other than time and the occasional script that appears to clean itself up, like the large jump you see in the picture.

Finally, I have a video that demonstrates the instance increase as well as one final way the memory leak(s) present itself(themselves).

The worst way possible:


The SERVER SIDED UntrackedMemory goes up when you reset
This is done on a private server. The rest of the examples are from the same session.
(Sorry for the quality and speed, the 10MB requirement is rigid.)

What I try to demonstrate is that the instances on the left hand side go up by 10,000, and worst of all, as well as what seems to be the biggest issue, Untracked Memory increases. Even worse, is it doesn’t happen every time, but heavily impact servers. Also, when a player leaves the game the UntrackedMemory also goes up by around 40MB. Unlike like all the other examples I’ve shown, this one seems to lead to a dead end and is the least diagnosable, but is also the most impactful on the game.

If you look here:


SERVER SIDED UntrackedMemory gets giant. It can be as large as 5000 on older servers(12hours)

Where’s the code so we can help?” you may be asking. Well not only would it be bad idea for me to post all the code for the game, it would also be too much to reasonably ask someone to look though out of the kindness of their heart. Instead I thought maybe people could help me actually find the code that needs fixed based off of the information provided. (I’m desperate and don’t know what to do other than ask for help) Then once the kind of code that could be causing issues is found

I have some slight hunches, but after reviewing the code, disabling parts of the script to see if it would stop the issue, I didn’t have much luck. I’ve messed with quite a few things but didn’t do the best record-keeping on what results were. So if anyone has any questions that could help you help me I’ll happily answer them. My hope is once I can identify one of the memory leaks I can spot the rest of them.

To Close:
Horomori will also be assisting me in finding this issue. They may be more interactive as they are much more knowledgeable in scripting than myself and are more qualified in answering your questions, but I’ll do what I can.

Thank you for reading, and I’ll get all my thanks out in advance for your future assistance as well. I appreciate it as I feel I’ve of hit a wall.

Oh also here’s the game if that helps: Fling Things and People

6 Likes

Obvious question, but are you sure you’re deleting all the character’s parts when they are killed?

1 Like

Yes, the entire character gets destroyed when they die. The game doesn’t use any custom characters.

2 Likes

Have you tried using the Explorer and a breakpoint to see what remains after you reset?
You might want to use a simple logging script like this to see the instances that are being created and destroyed.

instancesCreatedSinceStarted = 0

workspace.DescendantAdded:Connect(function(p)
    instancesCreatedSinceStarted += 1
    print("New instance", p:GetFullName(), instancesCreatedSinceStarted)
end)

workspace.DescendantRemoving:Connect(function(p)
    instancesCreatedSinceStarted -= 1
    print("Destroyed instance", p:GetFullName(), instancesCreatedSinceStarted)
end)

Here’s me resetting in a baseplate:

1 Like

I have used workspace.DescendantRemoving that also printed everything removed. I believe everything in my character was removed from the workspace when I reset, but admittingly I didn’t check every little thing in the list and check what wasn’t removed… I have not thought of keeping count of everything that was added in the workspace though, as all the items around the map spawn in when the Server starts. I’ll stop all the items from spawning in and test what you have here, just to make certain all the parts in the Character is being destroyed.

I suspect there is something about how the graphics are set and that is what is causing the memory increase. In every test I did; the graphics settings were set to 10 (max).

In the first test, I joined the game, opened the Developer Console and waited 5 seconds before selecting the medium graphic setting in the in-game graphics menu. The memory usage had gone up 100MB+. To make sure the game was using about the same amount of memory usage I joined multiple games following these steps.

In the second test I did, I joined the game, opened the developer console when joining the game, checked to make sure the memory usage was as usual, waited 30 seconds, selected the medium graphics settings and then looked at the memory usage once more. It had increased 100MB+.

In all of the times I selected a the medium graphic setting, nothing in the game had visually changed. I suspect that this is what is causing the memory usage increase and that the same settings are being applied every time the character resets.

1 Like

Hey buster, just wanted to give you an update. Apologies for being slow.

So, after investigating using this idea I believe we have discovered something although we don’t know what it means yet. So, with toy spawns off in the game, this is what loads in:

Honestly, we’re not 100% sure what this means, as when looking at this picture you can see there are two RagdollLeftArm0, and both of them can actually be found in the workspace, but lead to the same instance.

Then after resetting TWICE:


As you can see more instances are added of a certain type (highlighted). And these added instances also can’t be found, although they exist somewhere. Also there are inconsistent numbers of instances because there are some scripts that do not run each time you reset.

We are busy investigating further, and are hoping this lead will be of assistance. Although we have taken a look at the script that would be causing this and have yet to find a problem.

1 Like

Now this took me a while to figure out, but I looked at specific causes of the increase in client sided Memory usage after picking the middle graphics option “Lesser Detail”. I compared and contrasted each section of the client sided memory and this is what I found.

CoreMemory:



595 to 600. No real change

PlaceMemory:



Now that’s pretty hefty. 556 to 608. Most of which seems to come from LuaHeap, although this isn’t what I believe makes LuaHeap continuously rise, it is clearly a factor in greatly increasing it immediately, so something we’ll definitely look into.

However this doesn’t account for the additional 50 MB of memory that you’re claiming, and I have also seen, that the client memory increases by. So what is it from? Well the only explanation I have is this.

CoreScriptMemory:



Not sure there’s much we can do about that or if it’s even a problem. However thank you for pointing out the memory usage increase you noticed just from the detail chooser. After looking at the script we should be able to obliterate the LuaHeap induced by the graphics options.

Some of what you are seeing is just normal ROBLOX (namely PlaceMemory, besides the possibility of parts not being removed).

garbagecollect("count") will tell you how much memory is being used by Lua.
Besides that, ensure you are cleaning up connections and the like, and ensure that any and all tables you have aren’t massively inflating due to an oversight on your end. I can’t say much else from an outside perspective.

1 Like

First of all, I’m pretty sure some of the memory depends in the Device you’re using, But most of the stuff that can cause Memory leaks is:

  1. While loops without a minimal yielding (Still leaks memory even with small yielding)

  2. The whole game works only using a Single Script. (Although it can still work with only one script, You would need to optimize it alot) I recommend using the task Library if you haven’t. As it’s currently the best library for Spawning, Waiting and Delaying provided by Roblox.

  3. Bad network optimization, BasePart:SetNetworkOwner() can be useful for managing NetworkOwnership of BaseParts.

  4. Not disconnecting Connections when you’re not using them anymore, That’s something most people do. They usually create a new Connection (For example, RunService.RenderStepped:Connect(Callback)) whenever a certain event happens, But they don’t disconnect it.

  5. Using Instance.DescendantAdded. Yes, You can use it. But running a very “complex” function everytime a new Descendant is added is really expensive. I recommend you doing some checks before running it.

  6. PhysicsSteppingMethod. Roblox usually always calculates Physics 240 times per second. (Correct me if i’m wrong) But you can change it to “Adaptive” (Via Workspace > Behavior > PhysicsSteppingMethod > Adaptive) which will only calculate Physics at a very high rate only when needed. Whenever it doesn’t needs, It will keep it at 60 times per second by default. It’s really good as it’ll “automatically” manage it for you.

If you want some advice on what you can do, You can use some of the resources below:

  • Janitor - For managing Objects, Instances and Connections.
  • Task Library - A way better alternative instead of using spawn() and delay().
  • BufferModule - Great for managing Latency/Lag when using Tools.
4 Likes

This stuff shouldn’t go to LuaHeap afaik, it would go in Instances or PhysicsParts. Plus, calling :Destroy() is only necessary on the top instance, descendants get destroyed and have their connections removed too, so, destroying individual parts isn’t necessary if that’s what you’re suggesting.


@Zevris3600 @Horomori
There’s an important piece of not very well known info which might be the problem with PhysicsParts: When you :Destroy() an instance from the server, the instance is not :Destroy()ed on the client and will continue to exist in nil. This is just like setting the Parent property to nil, and it sounds like based on the context I have you’re not experiencing increasing PhsyicsParts memory on the server.

My personal recommendation for a quick and easy fix would be to use a RemoteEvent and use FireAllClients to tell clients to destroy stuff you need them to. This is not ideal though, and is better solved with related client code that specifically handles the cases you need, for example, if you have player code running on both the client and server which handles characters, you could share the same code on both ends for destroying the character when they die/respawn.

LuaHeap is all of the stuff that’s sitting in things like local variables and upvalues, stuff attached to connections, and, it can include stuff from CoreScripts and probably any other unrelated lua stuff.

You should check the specific memory categories Roblox makes for your individual scripts and see if any of your scripts are leaking memory. If you have a lot Roblox will group them up which is not very helpful, but, it’s better than nothing.

I would also recommend taking a look at the somewhat newer memory categorization feature, primarily debug.setmemorycategory in some of your scripts. I am not sure exactly how this works with the lua heap or if it includes locals defined after setting the memory category, but, it would let you identify specific places in your scripts.

2 Likes

@Horomori
Also in this particular post, you could take a look at the full name of these instances (Instance:GetFullName()) for debugging their actual locations in the workspace.

There is also now an Instace.Destroyed event which fires when the game destroys an instance, which differs from simply parenting it to nil.

I wrote a little snippet to just make a new table of all the full names and print it out:

local descendants = workspace:GetDescendants()
local fullNames = table.create(#descendants)
for _, descendant in ipairs(descendants) do
	table.insert(fullNames, descendant:GetFullName())
end
print(fullNames)

It sounds to me like your issue isn’t related to character spawning though, if your instance count goes up by 10k in only a single reset that suggests to me you have code creating a lot of new instances when the player spawns.

Plus, Roblox handles the destruction of default characters automatically.

1 Like

Soooo…
it took longer than I’d like to admit but I’d like to give an update.

I talked about this a little earlier but, there were objects duplicating such as RagdollLeftLeg0. In order to hone in on a single script causing an issue, we disabled all the other scripts. Then we found specifically wasn’t duplicating. Here is specifically what doesn’t change:

As you can see not only are the objects duplicated without resetting, and both lead to the same Instance, but then after resetting:

Now there’s 3 of every one of these instances. They will continue to grow in multitude and only two will ever show up in the workspace/explorer, and those two will lead to the same instance…

Here it is in the explorer:


Red indicates what has multiple instances and yellow is what doesn’t duplicate. Both of the yellow objects are not taken from a ragdoll template, which is how these instances got on the HumanoidRootPart in the first place.

Despite looking at the script for the longest time we couldn’t figure it out. However, it is due to this type of line of code:

IsaBug

For some reason unbeknownst to both of us, when you reset, which we THOUGHT was supposed to be equivalent to a Destroy(), does not destroy Instances on the HumanoidRootPart or the value in the Humanoid.

This code yields this innately on the character:
TestValue

But after resetting, which we thought would destroy the children of the character, instead yields this:
TestValue2
I am unsure as to why this occurs even after finding it to be the issue, but it’s probably due to my lack of knowledge. This seems to have solved one of the issues I have found above, which is shown in the video in my initial post. The SERVER UntrackedMemory does NOT increase by 30MB now when you reset.

Now you may be wondering why such a few number of parts would cause such an increase in memory. It may be due to the fact that the code put these parts on not only the player, but also created ALL the NPC ragdolls as well. So it may have been because it also added Instances to the NPCs as well. The other issues still persist, but I thought I’d give an update.

That’s all I tested for today. I plan to get more done for tomorrow. Just know I didn’t give up yet, so more advice would not, and is not falling on deaf ears! I just don’t have an update to give yet because I haven’t gotten around to it, as progress is slower than I’d like.

2 Likes

Those who are weary seeking memory salvation, there is none here for you.

Silly Analogy:
Not to be dramatic or anything, but I would describe looking for these memory leaks to finding yourself mysteriously stranded in the middle of the desert, meandering in any which direction, only to second guess yourself every step. Then, once you finally reach civilization you’re not sure if it’s real due to heat stroke, dehydration, or hunger.
Silly Analogy over.

Anyway, so the post right above this one was the discovery of our first memory leak. It was Untracked SERVER memory when you RESET or DIED. It would increase by 30MB. a couple of small tweaks fixed it. Unsure why only on the HumanoidRootPart and the Humanoid parts would persist but not the rest of the character.

Finally we found a second issue that I did NOT mention previously. When you leave the game it increases Untracked SERVER memory by another 30 MB. This is another problem since, y’know, you don’t want servers running poorly in a physics game. So let me show you what appeared to be the issue.

GuiandViewport
We disabled everything in a script and until this was disabled, the problem was resolved.
Explorer
Context for the code

Then we enabled everything we could that wouldn’t error the script, keeping the code above disabled. The 30MB increase when players left was gone. Don’t know why this time, just like last time. Apologies if the explanation is poor, but using the analogy earlier I just got out of the desert, and am slightly demoralized.

All the other “memory leaks” we experienced in the start of the post we are unsure if they are even leaks or just the workload needed to run the experience. For now we are just happy resolving the current two biggest issues that caused server memory usage to increase over time.

Sorry if this doesn’t help, and sorry if it would help if I included more context I just don’t know what information would assist someone experiencing the same problem.

Thank you to all of those who helped me and Horomori above. Even if I didn’t specifically reply to you just know that we definitely tried the strategies recommended as well as implement what information people provided.

1 Like