Is server memory meant to go up and never come down? For servers that are open for a long time

Is server memory meant to go up and never come down? For servers that are open for a long time

3 Likes

It depends.

If it’s steadily increasing over time, that could indicate thaht you have memory leaks somewhere. I.e. you’re allocating memory and not freeing it again when it should be freed. Here’s a very simplified example:

local t = {}
while wait() do table.insert(t, 1) end

This will just fill the table with more and more numbers, which obviously require memory to be stored which can’t be GC’ed, causing a memory leak. Unless this is part of your game’s logic, in which case it’s not a memory leak, that just means your design doesn’t use a constant amount of memory which is normal and fine. E.g. in a building game where players can create parts, obviously memory goes up as more and more parts are created. So you can’t just look at a graph of memory usage and determine if there’s a memory leak or not.

Memory leaks are unfortunately tricky to track down, in part because we don’t have direct control over memory in garbage-collected languages like Lua so we can’t just go through every single allocation and read the code to verify that there’s a corresponding freeing of that memory.

If memory usage is out of control and causing performance issues then you definitely need to deal with it, but if it’s just a slow trickle over many hours then it might not be worth the considerable effort it might take to fix it.

5 Likes

Yes, you can always force a shutdown if necessary.

https://developer.roblox.com/en-us/api-reference/function/DataModel/Shutdown

1 Like

Thanks for replying, if you have a table such as

local HitPlayers = {}
table.insert(HitPlayers,c)

and you don’t nil the table will this get auto gc’d?

Short answer, no.

If you keep (strong) references to objects in the table or anywhere else forever, then those objects won’t be GC’ed even if you Destroy them. The table itself will be GC’ed when all references to it are gone. That happens when you make all references to it refer to something else, or when the refs goes out of scope.

Make a variable refer to something else:

local t = {hitPlayer}
hitPlayer:Destroy() --Won't be GC'ed 'cause hitPlayer refers to the Player object
hitPlayer = nil  --Now there's only 1 ref to the Player object, which is in t 
t = nil --No variables refer to the table, so t and hitPlayer can be GC'ed
while wait() do end --Prevent the script from ending, which would make t and hitPlayer go out of scope 

Make the variable referencing the table go out of scope:

local t = {hitPlayer}
hitPlayer:Destroy() --Won't be GC'ed 'cause hitPlayer refers to the Player object
hitPlayer = nil --Now there's only 1 ref to the Player object, which is in t 
--The script ends here, so t goes out of scope, getting GC'ed, removing the ref to `hitPlayer`

Make the variable referencing the table go out of scope (again):

do
    local t = {hitPlayer}
    hitPlayer:Destroy() --Won't be GC'ed 'cause hitPlayer refers to the Player object
    hitPlayer = nil --Now there's only 1 ref to the Player object, which is in t 
    --The scope ends here, so t goes out of scope, getting GC'ed, removing the ref to `hitPlayer`
end
while wait() do end

Make the table not refer to the object:

local t = {hitPlayer}
hitPlayer:Destroy()
hitPlayer = nil --Now there's only 1 ref to the Player object, which is in t 
table.remove(t, table.find(t, hitPlayer)) --t no longer refers to hitPlayer
while wait() do end

If you want to prevent a table from preventing GC, look up weak tables.

3 Likes

I will take a look into weak tables?
local HitPlayers = {}
table.insert(HitPlayers,c)
HitPlayers = nil
So this won’t get gc’d i’d have to remove everything in the table first?

Since there are no references to HitPlayers, it can itself get cleaned up. When that happens, HitPlayers it won’t refer to anything anymore. If it was the last thing that referred to whatever is stored in c, then that will have no references left to it so it can be cleaned up.

But since you’re not setting c to nil, that variable will continue to refer to whatever it stores, keeping it in memory forever.

Of course all of this only matters if the lines of code that follow contain an infinite loop to prevent every var in the script to go out of scope, or a reference to the variables HitPlayers and c that doesn’t get cleaned up, such as an event connection. So if this is the entire script:

local HitPlayers = {}
table.insert(HitPlayers,c)
HitPlayers = nil

then everything gets cleaned up because after HitPlayers = nil runs the script is done and all the variables go out of scope, removing their references. If instead you have

local HitPlayers = {}
table.insert(HitPlayers,c)
HitPlayers = nil
while wait() do end

then the script will never end. The table that HitPlayers refers to will be cleaned up because you remove the reference to the table (you make HitPlayers refer to something else). You haven’t defined c in the snippet and all of this is assuming that c is some value. That value will never be cleaned up because you never set the variable c to refer to something else.

2 Likes

‘c’ Would be refer to a character,

“But since you’re not setting c to nil, that variable will continue to refer to whatever it stores, keeping it in memory forever.”

I would need to set c to nil aswell or can I just nil the table for it be cleaned up?

local c = Character
local HitPlayers = {}
table.insert(HitPlayers,c)
HitPlayers = nil

Yes. But there’s more “potential for memory leaks” in tables not being cleaned up. A variable referring to a table can potentially prevent 1000s or even more things from being cleaned up, while a variable referring to a single object that doesn’t prevent other objects from being GC’ed at most keeps that one object in memory.

1 Like