I had no idea about this. Thank you so much for sharing your knowledge! Much appreciated.
No problem, I just wanted to make sure people knew about this before the problem arised for them.
This is actually really nice to know!
Should we do the same thing with the players character?
I was a little confused on that part.
Anyhow good stuff! Thanks!
Yes, sorry for not explaining that thoroughly. i will re-edit it later
Player.CharacterRemoving:Connect(function(Character)
-- Pretend I'm doing other stuff for the character before destroying it
Character:Destroy() -- Memory usage has now been free'd up!
end)
Workspace.PlayerCharacterDestroyBehavior
, the default is just disabled right now.
Ah haven’t seen that, good catch!
However, still a few problems:
So I guess it still holds a need partially.
The documentation should also definitely update their page then.
Edit: After reading the update section a bit, some people have claimed that sometimes it wouldn’t even destroy the character automatically on the server:
Some people have also claimed this change has meant that ragdolls for characters are more of a hastle, so it may be preferred to set workspace.PlayerCharacterDestroyBehavour to a different behaviour and again only destroy the character/player when you are done with it.
It’s not a bug and is completely intentional by roblox.
They want you to keep the character/player object even after leaving the game because you may want to use it on their removing events. But destroy them manually once you have used them for your needs.
Problem is, many people did not know roblox doesn’t destroy it automatically.
Roblox’s documentation quote on quote says:
“The engine does not automatically destroy player and character objects”
I didn’t even think about replication!
Although, you’re fix may break if someone uses Immediate
SignalBehavior or Roblox changes the default for deletions. You’d want to defer and wrap those calls.
local Players = game:GetService("Players")
local function Destroy(Object: Instance)
task.defer(pcall, game.Destroy, Object)
end
local function OnPlayerAdded(Player: Player)
Player.CharacterRemoving:Connect(Destroy)
end
Players.PlayerAdded:Connect(OnPlayerAdded)
Players.PlayerRemoving:Connect(Destroy)
I’d also mirror this on the client if destroying isn’t replicating.
Yes this is actually similiar to a code sample from the documentation.
Great idea.
Thank you to @index_self for mentioning the announcement update given by roblox.
Currently, from what I’ve gathered from research on the announcement, many people are experiencing issues with the new implementation.
So it would be best to disable both player/character destroy behaviour on the workspace and set up this manual destroying yourself.
This may change later on as roblox updates, but right now it’s not 100% safe to use the default property.
I got the part where I have to manually destroy the character for it to be cleared from memory, but do I need to destroy the Player instance aswell?
Yes, both are not automatically destroyed by the engine.
I’ve updated the tutorial a bit, I suggest giving it a re-read for the best approach.
Don’t these instances simply get garbage collected once all references to them have closed?
In the example bellow, the memory usage goes up very fast, but ends up stabilizing. Just setting the Parent to nil, and ensuring there is no reference to the part, is enough for it to be cleaned up
while true do
for i = 1, 100 do
local Part = Instance.new("Part")
Part.Parent = script
task.delay(1,function()
Part.Parent = nil
end)
end
task.wait()
end
If you remove the task.delay function, the memory never stops going up
It’s not good practice to just parent things to nil instead of destroying them, but it does not necessarily cause memory leaks
Its a bug because they could automatically remove it after the functions are called. there is no player(client) so there is nothing to do with it, maybe only with the physical character but not the player
This is true for regular objects such as baseparts, but the engine specifically states that they do not garbage collect player/character objects:
Hmm, possibly. But I do not have access to the bug reports unfortunately.
The problem is when there persists a strong reference to the underlying instance through the engine. Ideally we shouldn’t need :Destroy()
. I’m personally of the mindset that it’s the engine’s single most glaring design flaw; and that they really ought to stop expanding its utility—and start investigating other means of treating instances in a more GC compliant fashion.
In the beginning, we did not have :Destroy()
. Afterall, the inherent advantage of landing on a garbage collected language, was that scripters would not need to bother themselves with the woes of manual memory management. Unfortunately, it soon-after became apparent that functions connected to signals could possess strong references back to the associated instance—and this cycle of references would prevent that memory from ever being considered eligible for garbage collection. Thus :Destroy()
was added. At this time, with what limited utility it had, it could be seen almost as an extension of the events API. Sure, the language is garbage collected; but in the case that you happen to open a connection, either remember to explicitly close it, or call :Destroy()
to sever everything bound to the instance’s signals. Undesirable; but simple enough.
In recent years however, it’s unfortunately become again apparent that there exists yet more possibilities for memory leaks within the engine. And it’s been Roblox’s (I think questionable) response, knowing full well the magnitude of this problem, to expand on the behavior of :Destroy()
to likewise treat these less common edge-cases. So whereas :Destroy()
was once the niche case treatment, it is now much closer to the general rule. But :Destroy()
isn’t really a generic cleanup method. As is clear from its evolution, it’s really just a bundle of patchwork solutions targeted at addressing very specific pitfalls. So these unprecedented expectations are actually damaging; because they become the self-same justification for Roblox to further expand the method’s largely undocumented influence.
I agree with everything you’ve said, and it’s a shame that roblox requires us to handle the stuff it should already be handling for us. But I guess there’s nothing we can really do about it.