Memory usage high and getting higher!

I’ve recently been making a game but noticed something odd about the lag.

The resting server memory on my game is 650, and as more people join, it goes to 700+, which causes a ton of server lag and makes my game really unplayable. I also tested the game in a solo server where no one joined or anything and the server memory went from 650 to 670 in 10 minutes! Does anyone know what could be causing this

I don’t know if this is a memory leak or something else. My game just seems to really like eating up memory.

probably poor server code if I had to guess

It would be hard to say without some more info on your game.

What scripts are associated with players that could be the source of the leak? If it occurs when players leave and join, and affects the entire server, I’d start by looking into any code or connections that are replicated to/for a player on join. If the memory increases only when they join, and doesn’t rise when there is nobody joining, it is likely that you have too many connections for each existing player on the server.

Edit: Constant rising of your memory levels (beyond rendering) absolutely indicates a memory leak. Check out your LuaHeap and see if much of the memory is being taken by script connections. Although heap should clear it’s own garbage after too much memory is consumed, if you’re not disconnecting connections or leaving threads open that should be closed, it will never become garbage and therefore never be collected.

1 Like

Well first of all I’m not a professional at memory leaks or anything technical at all :confused: can you explain what could cause a memory leak?

Also there’s 100% a memory leak going on somewhere. I checked into one of my servers from when I first started it, It jumped from 653 to 748 within about 20 minutes

Memory leaks happen when code constantly creates memory that is never destroyed (i.e in a loop, or when connections occur)

Let’s say you have a part and you connect a touch event.

local PartsThatTouched = {}

Part.Touched:Connect(function(TouchingPart)
table.insert(PartsThatTouched, TouchingPart)
end)

This code will add any parts that touch another part to the table PartsThatTouched.

However, since there is no code that will ever remove any of those stored (“Memorized”) references, each new addition consumes more memory (until eventually you have consumed too much, and lag begins)

There are many less obvious examples that can cause this. Creating connections inside of other connections but never disconnecting either is a great example. Each time you :Connect() to an event, that connection has to be stored in memory. If you have connections attached to players who join (i.e connecting a Changed event to their leaderstats), each one of them will take memory. If those connections have memory leaks inside of them, the problem becomes exponentially worse.

There is a lot to learn when it comes to efficient code, but I hope I’ve done a good job explaining this specific part to you.

I’m happy to help answer more questions, but there will likely not be only one solution for solving your lag issues.

Edit: It should also be noted memory stats from studio are not reliable. Studio stores the memory for both studio and client on your client during play tests. (your client becomes the server). Because of this, memory will be much higher in studio and not representative of a real server.

6 Likes

Oh my god. How have I never known this my entire life. Welp I’m officially a crappy scripter because I never even knew memory leaks existed until now. So I’m guessing code like this:

Would cause a lot of memory leaks?

At just a glance, no. This code likely would not “leak” much memory. It will consume a lot, but not leak.

Leaking occurs when you create the need for more memory during the life of a script. Without having given it a full read-through, I would not say code like that would have issues with leaking.

A single connection, or what may look like many when in code, is not actually much memory to the server. The issue would come when you are constantly creating the need for new memory but never freeing any.

Edit: However, that code absolutely would be a very large leak if you were to create all of those connections every time someone joined, or every time another connection were to occur, if you never disconnect them after they are not needed. This would be the equivalent of copying that script and pasting it into memory every time someone joins.

1 Like

So then this would be a huge issue, because the remote events are firing a lot because of people shooting guns. Wouldn’t any leak be a huge issue though?

The remote events firing are not an issue. Here is an example of code that would leak memory similar to your situation.

RemotEvent.OnServerEvent:connect(function(Player)
Player.Character.Humanoid.Changed:connect(function()
print('Hello!')
end)
end)

If you were to call this event repeatedly from a client, every time it is called a new Changed connection for the humanoid is created on the server. However, all of the previous Connections created when the Remote Event was fired will still exist in memory (and connect on every change)

This means that if you were to fire the event from a client 100 times, the server will fire the Changed connection 100 times for every 1 time there is actually a change (because you created 100 connections). This means you have consumed 100x the memory you need, and will consume more every time another Changed connection is created inside of the server event connection.

A single connection to each remote event will not leak memory, because it is never re-created (and therefore always takes the same memory, even if it’s repeatedly called). It would only be an issue if every time the Connection occurs, you consume memory that is never freed. If the connection does not consume more memory every time it is fired, it will not be an issue.

Edit: It should also be noted that creating connections are not the only thing that will consume memory. Anything you create that exists outside of the function you want to run (i.e a non-local variable, adding items to a non-local table) will continue to exist in memory once the function ends.

function ConsumeMemory()
     Hello = "I consumed memory!"
end

ConsumeMemory()

print(Hello)

will print “I consumed memory!” because the variable was not local, and will exist after the function has ended.

function WontConsumeMemory()
     local Hello = "I consumed memory!"
end

WontConsumeMemory()

print(Hello)

will print nil because the variable was local, and is no longer in memory.

The first time you run ConsumeMemory() it must define the variable “Hello” to be remembered by the script later. Since it was not done locally, when the function ends, that memory still exists.

In the second example, the memory to store “Hello” will only be taken until the function ends, and the local memory no longer exists.

There are times you will want to create memory that is not removed once a function has ended. However, these are the things that have the potential to create leaks. You must be careful that once the memory is no longer needed, you remove it properly.

What is this? C++ :skull::skull::skull: but in all seriousness that is really important because that’s a memory leak.

If you have a trigger part thing that you don’t need anymore, delete it. you can also do :Disconnect() to disconnect an event.

Would this apply for client code / local scripts too?

Server and client have their own memory. Client is limited by user’s device memory, server is limited to 4-6000mb (iirc). But yes, memory leaks on the client can lag the client.