Folks,
Over the last couple of weeks I have had to solve three fairly major memory leaks that, in the end, were all caused by the same issue so I thought it would be worth mentioning it here to help anyone else who might have the same problems in their code.
One of the things about Garbage Collection is that it appears to be a panacea to finally quash all memory bugs. Unfortunately, it is not! While it is plainly a lot safer and more convenient that having to remember to free objects manually, it is still important to check that one is not leaking memory before releasing anything into the wide world.
I'm my case the issue was to do with using addEventListener() to listen to events from global objects (or at least from objects that were outside the scope of the object doing the listening). I hadn't realized that when using:
xxx:addEventListener("eventName", yyy.func, yyy)
the xxx object will keep a hard link to the yyy object. This will prevent yyy from being garbage collected while xxx remains alive. To use one of
@atilim 's demos here is an example:
YYY = Core.class(Sprite)
local XXX=Sprite.new()
function YYY:init()
self.proxy = newproxy(true)
getmetatable(self.proxy).__gc = function() print("collected!") end
XXX:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
end
function YYY:onEnterFrame()
collectgarbage()
end
-- create an object
YYY:new()
stage:addEventListener("mouseDown", function()
XXX = nil
collectgarbage()
end) |
Notice how, even though neither XXX nor YYY are added to the stage, the garbage collector cannot collect YYY until XXX is released.
@atilim, is this what you intended for addEventListener(). My expectation was that it wouldn't keep objects alive. Could you not use a weak link to avoid this?
Anyhow, I hope this might save some other developers a few hours of hair loss if they find they have to track down similar leaks.
best regards
Comments
I think here the tricky part is yyy.func. Because if yyy.func accesses a local variable as an upvalue, then that upvalue is also reachable.
If you look at this code:
I think the current implementation (strong references) is correct. addEventListener idiom is commonly used in JavaScript and ActionScript and both implementations store a strong reference. But ActionScript gives an option for weak references also. And I can also give an option for weak references for experienced users.
Likes: fxone, zvardin
The important thing is for users to realize what's happening. It might be worth an explicit mention in the docs?
best regards
planet:addEventListener(Event.MOUSE_DOWN, mouseDownFunc, planet)
which is safe since planet now contains a reference to itself. This does not prevent planet getting garbage collected. But the third argument could be anything so perhaps Gideros could output a warning if the third argument (if present) is anything other than the object itself?
If you had
planet:addEventListener(Event.MOUSE_DOWN, mouseDownFunc, star)
then planet contains a reference to star. However, if both planet and star are removed (and nilled out) at the end of the level then they can both be garbage collected as they are an island and Lua has no trouble with islands (I think).
The only danger is if planet is a persistent object and we want to remove star. The only option here is to use planet:removeEventListener. I think it is good practice to remove event listeners from any object that persists across levels at the end of each level/room.
Likes: atilim
https://github.com/gideros/gideros
https://www.youtube.com/c/JohnBlackburn1975
And about docs, you're right.
Also if we store weak references by default (or globally), then this implementation:
Using weak references, you have to take care of removeEventListener before the object to be collected ,otherwise will access freed memory adress, is that right?
https://sites.google.com/site/xraystudiogame
best regards
"If an object persists across levels/rooms, its event listeners should be removed when the level/room is deleted, then regenerated at the beginning of the next level/room"
https://github.com/gideros/gideros
https://www.youtube.com/c/JohnBlackburn1975