Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
The proper way to clear a parent group and prevent memory leaks? — Gideros Forum

The proper way to clear a parent group and prevent memory leaks?

MellsMells Guru
edited May 2012 in General questions
I was wondering how do you clear your parent groups to prevent memory leaks?

As a beginner, I was thinking :
  1. Get all children of a parent sprite (something that might be achievable thanks to the snippet shared by @MikeHart
  2. removeChild -> But was does 'remove' really mean in the documentation? Should I 'nil' the objects also?
  3. Get all listeners that belong to the parent and its children, and remove it (will need to search how to get those listeners)
  4. Am I on the good way? How do you handle that task?

    If somebody has links to give me so that I can study some code (snippets), that would be very helpful.

    Thank you, community.



twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps

Comments

  • ar2rsawseenar2rsawseen Maintainer
    Hi ;)
    Well object is garbage collected (aka removed from memory) when there a no references to this object.
    When you add object to a parent, you create a reference to this object, so what remove function does is that it removes this references.
    You only need to nil in cases, where you created your own reference.
    For example:
     
    --no need for nil
     
    --create object
    local sprite = Sprite.new()
    --add create reference
    stage.addChild(sprite)
    --remove reference
    stage.removeChild(sprite)
     
    --need to nil
     
    --create object
    local sprite = Sprite.new()
    --add create reference
    stage.addChild(sprite)
    --creating your own reference
    stage.sprite = sprite
    --remove reference
    stage.removeChild(sprite)
    --need to remove your own reference
    stage.sprite = nil
    When there are no references to object it is garbage collected and all event listeners should be removed automatically, you don't have to do anything else. ;)

    Additionally, if, for example, a parent doesn't have any more references, like in case with scenes, but it still has children, then parent and all children are garbage collected, again you don't have to do anything.

    I guess only stage is the one left, that is never removed.
  • MellsMells Guru
    edited May 2012
    That explanation is crystal clear.
    Thank you very much!

    Edit :
    @ar2rsawseen
    --create object
    local sprite = Sprite.new()
    stage.addChild(sprite)
    stage.sprite = sprite
    stage.sprite:addEventListener(Event.MOUSE_UP, self.onMouseUp, self)
    stage.removeChild(sprite)
    stage.sprite = nil
    Is the event listener automatically garbage collected in that case because there is no reference to stage.sprite anymore or shoud I add
    stage.sprite:removeEventListener(Event.MOUSE_UP, self.onMouseUp, self)
    ?
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • ar2rsawseenar2rsawseen Maintainer
    Yes sprite is removed and event listener should be removed automatically.
    Only thing when you should remove event listener is when you want to only remove this listener without removing sprite.
  • MellsMells Guru
    edited May 2012
    I haven't done any optimization yet, I have tried to print garbage collection with math.floor based on some advices that I have read on the forum.
    1. What does this number represent? What is the unit?
    2. What is a decent memory usage, what is considered as too much for the iPad?
    3. Can I find somewhere recommended numbers (printed by garbagecollection("count") per app style (ex : interactive book avg -> ?)
    4. Are there other metrics that I should track in order to keep my app optimized?

    Right now I am using the scene manager with 2048x1536 images (plus a few layers) and my app displays ~350. Already too much?

    Any help would be great,
    Thank you.
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • The figure is (I believe) the amount of dynamic (heap) memory (in kilobytes) allocated to your app. Obviously the lower the number the better, usage will vary on a case by case basis so it's impossible to say what is "good" or "bad", other than "good" is working at a reasonable speed on the desired target hardware - "bad" is not :)

    The other metric for app optimisation (and probably more important from a users perspective) is frame rate, by default Gideros can work at either 30 or 60 frames per second (fps), there are plenty of examples of how to calc and display your app's framerate in the forum, most app's wont achieve a solid 30 or 60 fps, but the key is to make something feel as smooth and as responsive to the user as possible.

    Optimising for speed is usually counter productive to optimising for memory, as you can often pre-calculate items to make access faster at the expense of memory, I'd concentrate more on optimising for speed / responsiveness rather than memory and then only when you feel you need it. A lot of times the best optimisations can occur at the logic level rather than little "tricks" to make code run a bit faster, as often there is a "better" way to do something, however that said - employing good lua programming practices (keeping references to library functions as locals, not declaring locals inside loops, watching for excess operations / calculations being performed (especially inside nested loops) should ideally be the goal of any conscientious professional programmer. :)
    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
  • avoavo Member
    also @Mells that count number does not include texture memory used, you can see that in either Eclipse or Xcode (I'm assuming since I haven't actually used it).

    I don't have much experience but I don't think 350 is a lot. In the beginning I was stressing about managing memory and everything but then I realized I wasn't even using very much and I was spending a ton of time worried about 50kb or something when a single texturepack can be many mb of memory :P

    I tried to stop worrying so much and just watch out for memory leaks and the actual speed of my app so I could get things done faster. :)
  • MellsMells Guru
    edited May 2012
    I will keep your advices in mind, thank you :)
    About removing sprites @ar2rsawseen, I have another question that probably looks simple to developers here but this is ok if I look like a beginner, I will ask :
    local Group = Sprite.new()		
    self.addChild(Group)
    anotherGroup = Group
     
    -- nil
    Group = nil
    self.addChild(anotherGroup)
    is it the same as :
    local Group = Sprite.new()		
    self.addChild(Group)
    anotherGroup = Group
     
    -- Remove
    self.removeChild(Group)
    self.addChild(anotherGroup)
    Also in both cases :
    1. if Group has children, does it mean that after copy anotherGroup has the same children?
    2. if Group has a parent, does it mean that after copy anotherGroup has the same parent? Do I need to add it as a child of self like I'm doing in the example?
    Thank you
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • CarolineCaroline Guru
    edited May 2012
    @Mells - I'm not sure about the memory question, but I think you have a misconception.

    anotherGroup = Group

    does not make a copy of Group.

    There has only been one memory allocation - local Group = Sprite.new() - that allocates memory.

    anotherGroup is only pointing at the original memory block.

    For example, try this code (you'll have to make your own dog.png image):
    local dog = Bitmap.new(Texture.new("dog.png"))
     
    dog:setPosition(200,200)
    stage:addChild(dog)
     
    local anotherDog = dog
    anotherDog:setPosition(50,50)
    The memory for "dog" is allocated on the first line, and where "anotherDog" is created, it's pointing to the first "dog". So when you set the position of "anotherDog", it's actually changing the position of "dog".

    Secondly, I don't think it's good practice to create variables with an initial capital letter as in:
    local Group = Sprite.new()
    Capital letters are generally reserved for Class names, as Sprite.

    You see, Sprite is just a class definition - no memory has been allocated for Sprite. It's not until you say
    local group = Sprite.new()
    that memory is allocated for group, which uses the Sprite class definition.
  • MellsMells Guru
    edited May 2012
    Thank you @Caroline, I have learnt a lot.

    So if I understand well :
    local group = Sprite.new()		
    self.group = group
    self.addChild(group)
    is exactly the same as :
    local group = Sprite.new()		
    self.group = group
    self.addChild(self.group)
    Exactly the same memory allocation will be given as reference, right?
    So the important thing is the memory allocation and having one way (or more) stored to point to it later.


    My last question is about what's happening behind the scenes :
    local group = Sprite.new()		
    self.addChild(group)
    -- then later
    self.removeChild(group)
    is it different than :
    local group = Sprite.new()		
    self.addChild(group)
    -- then later
    group = nil
    Which one should I use?
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • @Mells - yes - but I worry about how you are defining "self". "self" should be used in a class that you have defined. Is that how you mean to use it?

    I don't know the answer to the second. I do know that the first one "feels" as if it's better code. Because you do something, then reverse it out.
  • evsevs Member
    @Mells

    if you run this
     
    local group = Sprite.new()		
    stage:addChild(group)
    -- then later
    stage:removeChild(group)
     
    print()
    print("Group:", group) -- table: 0xNNNNNNNNN
    print("Children:", stage:getNumChildren()) -- 0
     
    local group = Sprite.new()		
    stage:addChild(group)
    -- then later
    group = nil
     
    print()
    print("Group:", group) -- nil
    print("Children:", stage:getNumChildren()) -- 1
    It will show that a proper clean up needs both
     
    stage:removeChild(group)
    group = nil
     
    print()
    print("Group:", group) -- nil
    print("Children:", stage:getNumChildren()) -- 0

    cheers

    evs

    Likes: chipster123

    +1 -1 (+1 / -0 )Share on Facebook
  • Thank you all for your help.
    @Caroline yes, it was in a class, things are working fine now.
    @evs I see, I am a still a bit confused with the garbage collector, what remains in memory when the stage is cleared, function scope, etc... so your post was helpful.

    I was still evaluating Gideros until now, and I have decided to go with it for my first set of 2 applications.
    Thank you Gideros Team & community :)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
    +1 -1 (+4 / -0 )Share on Facebook
Sign In or Register to comment.