Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Help with memory leak — Gideros Forum

Help with memory leak

Tom2012Tom2012 Guru
edited February 2013 in General questions
I've got a monster memory leak in my game.

Upon finishing the level, and restarting the same level, the memory usage increases by around 800.

I'm using scene manager, stopping all timers, keeping everything in the scene scope.

There's something glaring that I've missed - are there any obvious gotchas I can look into? Or any way to 'see' what's causing the memory hog?

Thanks

Comments

  • ar2rsawseenar2rsawseen Maintainer
    edited February 2013
    @Tom2012 garbage collection is not done instantly, although a constant increment may signal that there is a memory leak, are you sure it does not decrease over the time?

    Check if you don't create any global objects (maybe forgot to type local before variable name), especially when storing textures, sounds or fonts.

    But for now, don't know a better way of debugging this as commenting out the parts of the code and see if that changes
  • john26john26 Maintainer
    You can find out when a variable is actually collected like this
    function MyClass:init()
       self.proxy = newproxy(true)
       getmetatable(self.proxy).__gc = function() print("collected!") end
    end
    :
    foo=MyClass.new()
    In this case, when foo gets garbage collected it will print "collected!". You should add some collectgarbage commands to force the collection early.

    Likes: ar2rsawseen, talis

    +1 -1 (+2 / -0 )Share on Facebook
  • @john26 yes completely forgot about that, good one
  • In my case, I grouped my objects into separated parent sprite object,when restart level try checking all child object is destroyed or not before recreate them.
    So you can print grpObject:getNumOfChildren when restart level to see the difference (to check if your obj is doubled or not)
  • you can also force the garbage collector to run with the
    collectgarbage("collect")
    command and return the amount of memory used with
    print(math.floor(collectgarbage("count")))
    command.

    Because of the way the GC works, you won't get an accurate reading until it's been run a couple of times, so you'll often see things like
    collectgarbage("collect"); collectgarbage("collect")
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • bowerandybowerandy Guru
    edited February 2013
    Folks,

    I thought I'd put together a little bit of instrumentation code that allows you to dump a count of all the objects in existence at any time, listed by class name. That way you can compare the snapshot at different points in your app to see what types of object are leaking.

    The code relies on a modified version of the classes.lua that Gideros uses internally (this was published by @atilim in another thread a couple of weeks back). It also relies on the proxy hack by @john26 above.

    Unfortunately, due to a limitation in the way the built-in classes are currently handled, the code doesn't list instances of these classes, only your user-defined ones. I've posted a question asking if this can be changed, but I thought the existing version might be useful anyway, so I'm attaching it here.

    BTW, don't leave the "leaky.lua" in your project when you're not debugging a memory leak as the extra work it does will be detrimental to overall performance.

    best regards
    zip
    zip
    BhLeaky.zip
    26K
    +1 -1 (+7 / -0 )Share on Facebook
  • Wow thank you. Very useful posts here.

    Going to tackle the monster leak from hell now. :D

    Dislikes: Yan

    +1 -1 (+0 / -1 )Share on Facebook
  • @bowerandy

    Thanks. What are the steps to getting this running in a project.

    I tried:

    1) Adding:

    classes.lua
    leaky.lua to
    bag.lua

    2) require "classes"
    3) require "leaky"
    4) use code:

    collectgarbage("collect")
    EventDispatcher.printAllInstances("message here")

    The error was:
    bag.lua:9: table index is nil
    stack traceback:
    	bag.lua:9: in function 'add'
    	./leaky.lua:31: in function 'postInit'
    	[string "property.lua"]:47: in function 'new'
    	main.lua:42: in main chunk
  • @Tom2012, that error is caused because you are not pulling in the classes.lua file correctly (I think).

    Try this:

    1) Add classes.lua, leaky.lua, bag.lua to your project
    2) Right click on classes.lua and choose "Exclude from Execution"
    3) Add a file called "init.lua" to your project with the single line, require "classes".
    4) Add the lines:

    collectgarbage("collect")
    EventDispatcher.printAllInstances("message here")

    somewhere in your project where you want to check the memory.

    When you run the project, you should see the message "MODIFIED classes.lua INITIALISED" appear before anything else.

    If this doesn't work, I'd need to see what line is actually causing the error in your code. In the example you show it is at line 42 in "main.lua".

    best regards
  • Tom2012Tom2012 Guru
    edited February 2013
    @bowerandy - thank you. That solved it. This is a very useful debugging tool. Will use this one a lot closer to releasing my app. Cheers!

    Edit: Fixed the memory leak.

    1) Needed to call vPad:stop()
    (I'm using TNT Virtual Pad)

    2) Needed to call Timer:stopAll()
  • @Tom2012, great I'm glad you got it sorted.

    Now, if we can just get those modifications to the class system then tracking memory should be even easier.

    Best regards
  • @bowerandy
    Hi, I was pointed to this topic because i'm having some issues with performance on my game.

    I tried to use your solution to examine memory leaks, but even after following the steps, I'm having this error:
    MODIFIED classes.lua INITIALISED
    bag.lua:9: table index is nil
    stack traceback:
    	bag.lua:9: in function 'add'
    	leaky.lua:31: in function <leaky.lua:30>
    	classes/gtween.lua:68: in function 'staticInit'
    	classes/gtween.lua:434: in main chunk
    Can someone help me?
  • I have the same problem as AxlFlame:
    Uploading finished.
    MODIFIED classes.lua INITIALISED
    bag.lua:9: table index is nil
    stack traceback:
    	bag.lua:9: in function 'add'
    	leaky.lua:31: in function <leaky.lua:30>
    	sources/libs/gtween.lua:69: in function 'staticInit'
    	sources/libs/gtween.lua:452: in main chunk
    Line 69 in sources/libs/gtween.lua cotains code:
    GTween.shape = Shape.new()
    If I switch off gtween.lua I have the same problem with SceneManager:
    Uploading finished.
    MODIFIED classes.lua INITIALISED
    bag.lua:9: table index is nil
    stack traceback:
    	bag.lua:9: in function 'add'
    	.\leaky.lua:31: in function <.\leaky.lua:30>
    	sources/libs/scenemanager.lua:252: in function 'init'
    	.\classes.lua:71: in function '__new'
    	.\classes.lua:81: in function 'new'
    	main.lua:49: in main chunk
    Line 252 in sources/libs/scenemanager.lua contains:
    self.transitionEventCatcher = Sprite.new()
    I've completed all these steps:

    1) Add classes.lua, leaky.lua, bag.lua to your project
    2) Right click on classes.lua and choose "Exclude from Execution"
    3) Add a file called "init.lua" to your project with the single line, require "classes".
    4) Add the lines:

    collectgarbage("collect")
    EventDispatcher.printAllInstances("message here")


    And when I put lines:
    collectgarbage("collect")
    EventDispatcher.printAllInstances()
    in my main.lau I see message:

    *** OBJECT SNAPSHOT 12/24/13 09:38:38 ***
    *** OBJECT SNAPSHOT END ****


    but nevertheless I have errors which I've shown above.

    I tested my app in Gideros 2013.06.1 and 2013.09.1.

    @Bowerandy, could you please help to find the solution for this issue?
  • chipster123chipster123 Member
    edited January 2014
    Folks,

    I thought I'd put together a little bit of instrumentation code that allows you to dump a count of all the objects in existence at any time, listed by class name. That way you can compare the snapshot at different points in your app to see what types of object are leaking.

    ...
    Well, it's nearly an entire year since this post and I FINALLY got around to using it and WOW, what a HUGE help!!! It helped me find and fix a memory leak. Thanks @Bowerandy

    @romka, have you figured this out? It looks like your project isn't using @bowerandy 'classes.lua' file because your output above is not showing the message
    MODIFIED classes.lua INITIALISED
    that bowerandy's modified classes.lua prints out.

    When all you get is
    *** OBJECT SNAPSHOT	Defined Classes	***
    *** OBJECT SNAPSHOT END ****
    That means the 'require "classes" ' line isn't really happening. I got the same thing when I started backing out the debug code.
  • piepie Member
    edited September 2014
    I think I was getting the same error as @AxlFlame: posting here for future reference.

    it happens when classes.lua is required in your project and it's loaded, (that's why you can read "MODIFIED classes.lua INITIALISED") but it's too late... :)

    I had init.lua in a subdirectory (on my harddrive, inside my app root directory). It should be placed in the root directory of your project on your harddisk (and in your project) otherwise it won't be loaded first (check it with Code dependencies > Call Order).


    p.s - thank you @bowerandy for sharing this :) it's really useful!

  • Maybe class.lua call first in init.lua but i think some "buildin class" like Shape.lua, it still do not have .class properties yet.
    (That why we have error: bag.lua:9: table index is nil -> this because of: EventDispatcher._allObjectsBag:add(self.class) - > here self.class is nil )

    I have a quick fix version here, and then it print out as normal:
    Bag=Core.class()
     
    function Bag:init()
    	self.contents={}
    end
     
    function Bag:add(object)
     
    	if object == nil then return end;
    	local tally=self.contents[object] or 0
    	self.contents[object]=tally+1
    end
     
    function Bag:remove(object)
    	if object == nil then return end;
    	local tally=self.contents[object]
    	assert(tally and tally>0)
    	tally=tally-1
    	if tally==0 then tally=nil end
    	self.contents[object]=tally
    end
     
    function Bag:getTally(object)
    	return self.contents[object] or 0
    end

    Likes: chipster123, pie

    Coming soon
    +1 -1 (+2 / -0 )Share on Facebook
  • Leaky.lua saved my day. Thanks @bowerandy and @vitalitymobile for a fix that worked.
    My Gideros games: www.totebo.com
    +1 -1 (+1 / -0 )Share on Facebook
Sign In or Register to comment.