MEMORY LEAK: Player connections are not disconnected on player leave

local t=setmetatable({},{__mode='k',})

game:GetService'Players'.PlayerAdded:Connect(function(plr)
	local x={}
	t[x]=true
	plr.CharacterAdded:connect(function(char)
		print(x)
	end)
end)

while wait()do
	local n=0
	for k,v in next,t do n=n+1 end
	print(n)
end

So if you run the above code in a server script in a one player local server, you can see that the even after the player leaves the game, the table is not garbage collected and thus the output remains ‘1’

BUT if you manually disconnect the CharacterAdded connection on PlayerRemoving, the output changes to ‘0’ after [a little time after] the player leaves the game:

local t=setmetatable({},{__mode='k',})
local cons={}

game:GetService'Players'.PlayerAdded:Connect(function(plr)
	local x={}
	t[x]=true
	cons[plr]=plr.CharacterAdded:connect(function(char)
		print(x)
	end)
end)

game:GetService'Players'.PlayerRemoving:Connect(function(plr)
	cons[plr]:Disconnect()
	cons[plr]=nil
end)
while wait()do
	local n=0
	for k,v in next,t do n=n+1 end
	print(n)
end

This doesn’t just happen for the CharacterAdded connection, it happens for ALL signals in the player object (well I only tested CharacterAdded, Chatted, and DescendantRemoving)

2 Likes

Is this one of the demons that have been making untracked memory climb stupidly high without going down? I never even knew this was a thing.

Might this not have to do with connections though? I thought that the Player instance is destroyed, thus disconnecting all connections and promptly having it garbage collected.

You have an upvalue reference to the Player instance. This isn’t a bug so much as an implementation detail, as this happens with all Instances (see this thread). What you might want is to request that Player Instances have their connections disconnected when they leave the game, since that’s the root cause here, but again, this isn’t a bug.

there is no upvalue to the player instance
locals are only designated as upvalues if they are used in a child function
the only upvalues in the first script are t and x

and yes, the bug is that ‘player connections are not disconnected on player leave’
the player object should be destroyed (it seems like the parent is locked to ‘null’), but the events are not disconnected

The player is being referenced indirectly by the CharacterAdded event. Since it’s still active, it’s preserving the Player instance. Even if it’s not an upvalue, this is still expected behavior for Instances and you would have to request it be changed for players specifically.

it really isnt being indirectly referenced
the table x and the function print(* that was another upvalue i missed) cannot be reached by the player object

expected behavior for instances is disconnectting connections on instance destroy
see the devhub if you don’t believe me

As OP explains this isn’t expected behavior. One would expect the player is :Destroy()'d when they leave the game, killing all active connections. This does not seem to be the case.

This isn’t a bug, it’s just unexpected behavior. You should reword this as a feature request and move it there.

What I’m saying is that I don’t think it’s defined that Players are actually destroyed when they leave.

I’m not disagreeing with that this is unexpected behavior, but if you go at this under the assumption that the Player Instance just quits existing rather than it being destroyed when the player’s removed, it makes perfect sense.

1 Like

id like an engineer to say that this isnt a bug and provide a reason for it because I find this hard to believe

no in that case the connections should still be gced by the garbage collector because I am storing no strong references myself
something else is keeping these connections persistent

If it’s not documented that players are destroyed, then you’re suggesting a new feature.

is it documented that it memory leaks intentionally?
are all bug reports feature requests under disguise because the wiki doesn’t rigorously and completely define the roblox engine? (which would be impossible anyways without the source code/ a complete description of the source code, white space etc)
so whats the point of bug reports then?

As I said, this is because you have an active connection to the Player instance, which count as a reference to the Instance. This is documented behavior and is an implementation detail.

1 Like

i think it would be best to wait for a roblox engineer to provide a reply on this

I don’t need an engineer to tell me that active connections act as references to Instances.

3 Likes

@Dekkonot :/ if it is okay with you, I think I do

edit btw just looked at your thread again:

do -- All good, this part will be GCed just fine
    local p = Instance.new('Part')
    p.Touched:connect(function() print("Touched") end)
end

@stravant says this is GCed, there is an active connection to an instance event
soooo maybe I actually don’t need an engineer?

Duplicate of Client-sided RemoteEvent memory leak (where a staff member tells you this is not a bug).