:LoadCharacter() leaks memory if you don't manually :Destroy() the old character

Not a bug. Player.Character = nil unassigns the character from the player. Character:Destroy() just parents it to nil.

2 Likes

Actually, it is. From what I gather, the player’s character stays in memory even after the player logs out. You have to call Model:Destroy() on the character so it can be garbage collected. Unless you’re using a custom spawn/respawn system, the Roblox default system will leak memory. Just calling Player:LoadCharacter() without called destroy first is what causes it.

So it seems that Roblox’s default system is missing a step. The problem is below the LUA interpreter so there’s not much we can do about it except explicitly call Destroy() on the character to try and keep the memory leak to a minimum.

3 Likes

Yes, I understand that, but I was referring to the message I was replying to.

1 Like

Yeah. I missed that. Sorry, been a very long day.

3 Likes

Thanks for posting this. Stumbled on this by accident, but I’m very happy I did :grinning:.

Our game [đŸșCoyoteđŸș] Yellowstone Unleashed - Roblox is affected by this.

Tested using the following code:

while true do
	local player = game:GetService("Players"):WaitForChild("apenzijncoolenleuk1")
	SpawnAnimal(...)
	task.wait(0.5)
	player.Character.Humanoid.Health = 0
	task.wait(0.5)
end

The ‘SpawnAnimal’ function clones the source model of a character, adjusts some properties and sets that as the player’s character to spawn as. No references kept live anywhere.

Running the above code causes a 1-2MB (never cleaned) server memory jump every respawn.
Setting the character’s parent to nil on death doesn’t fix it.

If anyone knows a quick workaround please let me know.
Thanks.

EDIT: Destroying the character doesn’t fix it either. I think we’ll have to wait
 :frowning_with_open_mouth:

2 Likes

let me know if this works

local Players=game:GetService("Players")
Players.PlayerAdded:Connect(function(plr)
	plr.CharacterRemoving:Connect(function(char)
		if char.Parent==workspace then
			char.AncestryChanged:Wait()
		end
		char:Destroy()
	end)
end)
Players.PlayerRemoving:Connect(function(plr)
	if plr.Parent==Players then
		plr.AncestryChanged:Wait()
	end
	plr:Destroy()
end)
3 Likes

No, sadly.


We’ll have to wait.

Weird because I see a memory leak in the client section as well.

4 Likes

I’m fairly sure you would also have to do player.Character = nil like the OP did, not certain though.

1 Like

Wow, all these years and I assumed this wasn’t the case and Roblox had cleaned up properly. This explains a lot


Hopefully this gets fixed. This must’ve been plaguing so many projects and the overall platform for years.

4 Likes

I’m skeptical. Explicitly setting Player.Character to nil seems superfluous, as subsequent LoadCharacter calls will reset the property with the latest Character Instance anyway. There also shouldn’t be any worry that the reference will keep the Character and the hosting Player Instance from getting GCd once nil parented, as I’d imagine this would only be problematic if the Character actually referenced back to the Player; otherwise the relationship would surely still be one directional, and not cyclic.

Anyone really. I replied to you, because you wrote that you were fairly sure that setting the Character property to nil would be necessary. Sorry for the confusion.

3 Likes

Did you mean to reply to me? Or to the OP?

3 Likes

Working with players and characters is a massive pain. Characters don’t load atomically and it was deemed technically challenging to reorder avatar loading events, and then there are regular findings that players and characters leak memory and Roblox doesn’t just destroy those objects which then leaves us to make strange boilerplate to do it properly - if we can even first agree on what’s necessary amongst us as mature developers, while novice developers are left entirely in the dark.

Properly destroying players and characters really needs to be escalated with Roblox having avatars as a major part of their future vision.

11 Likes

As mentioned, something really strange happens if you don’t set character=nil and call :LoadCharacter() in the provided example.
It’s possibly a bug all on its own?
If you fail to set it, the client spams that weird error about having no humanoid for moveto()

3 Likes

As was already mentioned earlier in this thread, the warnings you’re referring to originate from a local corescript. They have nothing to do with :LoadCharacter() being called, and are output any time the character is destroyed. This problematic script is likely reading Player.Character, which is why these warnings are silenced from properly setting the property to nil, when the Character is no longer present. I don’t think this would be related to any of your memory concerns.

2 Likes

Am I wrong in thinking that calling Player.Character:Destroy() should automatically nil out Player.Character?
Because if so, the behavior should be identical if you nil manually or not.
But the behavior does actually change.

1 Like

Hello.
Just wanted to mention that we are looking at introducing a mode where the engine will automatically call Destroy on players that are leaving and on characters that are being unloaded (after all the custom PlayerRemoving/CharacterRemoving callbacks are fired).

Until that is ready, we recommend manually calling Destroy.
We are also looking at ensuring that default character scripts disconnect any connection they make, without requiring a Destroy call.

42 Likes

hi
Do you have an example code that doesn’t leak memory?
Destroying a character still leaks memory it seems


3 Likes

You might have a different issue, you should open a separate topic with a description of your case.

4 Likes

This has been a headache for a while now, it’s great to see it finally being fixed!

Would it be possible to look into these as well? These make LoadCharacter() dangerous to use, which results in developers needing to call them in a separate pseudothread to avoid hangs. They were reported a while ago but never actioned on:

3 Likes

First destroy the character then set it equal to nil

plr.Character:Destroy()
plr.Character = nil

This disconnects all connections and destroys the character’s children

1 Like