Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Memory memory memory :) — Gideros Forum

Memory memory memory :)

moopfmoopf Guru
edited January 2013 in General questions
OK, I'm doing some work on making sure that my app isn't leaking memory and I'm finding it a real head-scratcher. For the purposes of explanation I have two main parts - the front-end and the actual game. I'm finding that the front-end is leaking around 100k each time it unloads. It was much more until I decided to add by own object cleanup - the front-end has a large number of sprites in the background which are added to a table and added to the parent sprite (FrontEnd) as they change continually and wanted an easy way to reference them - so I'm removing them from the parent and then nil'ing the table they're held in. This, I think, is part of the problem and a disconnect in my thinking about Lua memory management - I guess when using Objective-C I'm used to having to control it all myself but, not having some release method built in in Lua, you can't do that easily so I'm kind of stumbling around a bit. Would event listeners cause an instance's memory not to be freed up? I'm calling Timer.stopAll() as I'm aware that running timers could stop collection. Following @atilim's code on the forum I'm calling collectgarbage() 4 times before doing a collectgarbage("count"). But does anybody have any tips here on what to look out for that would cause things not to get released.

Secondly, I'm using a single packed texture for all the artwork. The front-end reports no change to the texture memory usage when it unloads. However, the game itself is reporting a 1mb increase each time it unloads. I am using some TTF text but I'm also using this on the front-end as well. Now I'm trying to think why this could be. I'm using a sprite cache for a lot of images, grabbing the texture regions and keeping them in a global for performance reasons (I've found this quicker than trying to grab texture regions as needed) but there are still areas where they are being grabbed as needed. Does that act of grabbing a texture region actually make an increase in texture memory usage? Is that correct? It's not what I'd expect.

So anyway I'm a bit confused and need to get my thinking straight. I haven't tried the XCode tools yet, I'm trying to see what kind of memory work I can do in Gideros itself first.

Sorry it's a bit of a long, wordy, post. Just want to get my head around Lua's memory management.

Comments

  • moopfmoopf Guru
    edited January 2013
    Interesting that nobody's commented on this yet. Are other developers here just ignoring whether their app leaks or not?
  • If you just nil something, and didn't call collectgarbage(), instead you go on and call the collectgarbage("count") then it's still the same.
    That is why you get the same texture memory when you do unloads. (i assume you didn't do collectgarbage() after nilling it), since it works fine for me.

    collectgarbage() can be so dangerous, for example, if you're using TNT particle engine, and assign emitter to local variable, and in the middle of the animation you do collectgarbage(), sometime the animation just stopped right away and event.complete won't be called.
    But it is okay, if the emitter is global variable, i think.
    The same thing applied on Timer, and MovieClip objects.
    That is also why i always applied Event.ENTER_FRAME only on stage, and not on my local objects. Because if i can remember, collectgarbage() will be called if memory warning is received. Just to make sure.
    Second reason, is that it is time consuming, so i won't call it each time i remove an object.

    Haven't tried XCode tools also.
  • If you just nil something, and didn't call collectgarbage(), instead you go on and call the collectgarbage("count") then it's still the same.
    That is why you get the same texture memory when you do unloads. (i assume you didn't do collectgarbage() after nilling it), since it works fine for me.
    As I said in my first post, I am calling collectgarbage() before doing collectgarbage("count").
  • Hmm...
    That is weird, i tried it again now, and it works just fine.
    No memory leak, goes up and down just fine...

  • Hmm...
    That is weird, i tried it again now, and it works just fine.
    No memory leak, goes up and down just fine...

    I suspect it's somehow related to having to remove all references to instances from tables as well (i.e. my first problem) but I can't be sure. It's difficult to know really, hence why I started this thread as I think I may have gaps in my understanding of how Lua (and by extension Gideros) decides when an object can be destroyed.

  • Perhaps something to do with your code eh. Won't know what happen then.

    Well, at least i can say that it is actually work just fine in Gideros.
    I always tried to manage memory usage, so i know for sure about it.
  • moopfmoopf Guru
    edited January 2013
    Perhaps something to do with your code eh. Won't know what happen then.
    Well yes, which is the whole reason for this thread. :rolleyes: I'm not suggesting that there's anything wrong with Gideros at all, I'm trying to understand better how memory is managed as I don't have the direct control and facilities for releasing in Lua that I'm used to.
  • tkhnomantkhnoman Member
    edited January 2013
    When you said things about Texture Memory on second paragraph, it just happen on my mind that it might be a problem. So it was caught on my mind and i forgot to colletgarbage it from my mind. :D

    Well, Gideros... works normally just like lua...
    Can't say much about how it works also. I just tried to maintain things normally. Global as strong, and local as weak, perhaps?

    I also do grab some of the texture on the first load, and do load others on the middle of the process, just like you did.
    But all of my objects, instead of audio, texture, and main layers, is assigned on local.
  • Well, Gideros... works normally just like lua...
    Can't say much about how it works also. I just tried to maintain things normally. Global as strong, and local as weak, perhaps?
    That kind of flies in the face of what I had to do with the front end - release the sprites from the table as well as remove them from the parent sprite in order to get them to release when the parent was destroyed (e.g. removed from stage and nil'd). The table was a self. variable, not a global.

    Whilst they are local maybe it's because they, in turn, are referencing a global, i.e. the texture region from the sprite cache. In which case this seems a little odd, as surely it's just storing a reference to the global - would that really stop a local (self.) variable from releasing its contents - if its contents were referencing something from a global?

    You see, this is why I'm a little confused about Lua's memory management.

  • tkhnomantkhnoman Member
    edited January 2013
    I don't think it works like that. self.variable considered local, right?
    I don't even release texture from a sprite. I just remove a sprite from it's parent.

    i also do referencing a global things, but to local variable, like:
    local something = something
  • I don't think it works like that. self.variable considered local, right?
    I don't even release texture from a sprite. I just remove a sprite from it's parent.
    Ok, so if you had a sprite object operating as a container (e.g. my FrontEnd class) are you saying that you remove all children from that before removing it from the stage and nil'ing it? That's not taken care of for you?

  • I just remove a sprite from it's parent, and didn't care about the children.
    It is explained somewhere i think.
  • Right, well that's what I'm doing as well. But I found I had to remove the children as well for some reason. I must be doing something that Lua or Gideros doesn't like that forcing things to be retained.
  • Now that makes me remember about something.
    On other lua engine, C*... If we call object:removeSelf() the object was cleared completely, and i don't need to call collectgarbage() at all.
  • @ar2rsawseen - Right, that's confirming what I thought above - because the children sprites are using a global called spritecache (as I said for performance reasons, as caching the texture regions seems to help a lot in my tests) it is forcing everything else to be retained. That's a bit of a pain and leads me down the path of creating my own dealloc methods, I guess. Which is pretty much what you have to do with Objective-C anyway, although the call to dealloc method is obviously built in there.
  • ar2rsawseenar2rsawseen Maintainer
    edited January 2013
    @moopg there is a great pool method that @atilim showed in his test project which I can say works great if you are creating great quantity of objects, like bullets.

    Maybe it would be possible to adopt something similar. How about creating a global texture manager class, where you request a texture and it get's loaded and cached if it's requested again. I don't completely know/understand your way, but this way you could manage texture loading/unloading/caching through class properties, by some keys as texture names, etc
  • @ar2rsawseen That's not a million miles away from what I'm doing already with a texture manager (although it's currently not class based). But fundamentally I'd still be referencing a global though (an instance of the class), wouldn't I? Which appears to be what's causing the retain problem.
  • Just to update as well, I've written some objectCleanup() routines in my children that the parent calls before removing them, and one on the parent itself to control this. I'm now down to a 40k leak on the FrontEnd class although, bizarrely, something I've done is now adding 200k onto the texture memory usage each time the FrontEnd is unloaded where as before it was 0k increase. Hah, going round in circles here :D I'll get to the bottom of it in the end. Once I've got things straight on the FrontEnd class I can then do the much more complex game class and sub-classes accordingly.
  • Hmm.. I still wondering about your problem. When i assign a texture to global, i don't want it to be removed at all.
    Did you do something like local newTexture = globalTexture ?
  • moopfmoopf Guru
    edited January 2013
    @tkhnoman What I'm going is loading a texture through TexturePack and then creating an indexed global table of the texture regions within that, e.g.:
    <pre>
    spritecache["connect.png"] = spritepack:getTextureRegion("connect.png")
    spritecache["highlight.png"] = spritepack:getTextureRegion("highlight.png")
    This is then used to reference the texture regions, without having to grab the texture region each time. I've found that this is better in terms of performance. However it appears that using this global in other sprites is causing them to be retained. At least that's my best guess from what I'm currently seeing.
  • OK, I think I'm barking up the wrong tree here. I've just run it with Instruments in XCode and I had only one set of leaks, right near the start of execution, and each one is a 16byte alloc coming from gaudio_WavOpen. I'd expect this, I always saw something similar with cocos2d-iphone. No other leaks were reported. Overall memory usage did rise over time however, presumably because of the extra texture memory being used, so I need to track that down now to see what's going on.

    But it looks like I'm getting hung up on something that is only being reported from the desktop player in terms of overall Lua memory usage. At least, I think so anyway...
  • tkhnomantkhnoman Member
    edited January 2013
    Hmm.. This is the code that i used, for MovieClip...
    I don't think it is much difference... Dunno, never tried to load the texture separately like that.
    	local imageGet= {}
    	for i = 1,#sheetResult.sets do
    		local fileName = sheetResult.sets[i]
    		--print(fileName)
    		imageGet[fileName] = Bitmap.new(sheetResult:getTextureRegion(fileName ..".png"))
    		imageGet[fileName]:setAnchorPoint(0.5, 0.5)
    	end
    Edit: Oh, well that's weird. Well, good thing then.
  • Right, I've converted all the app now to use the texture manager, which I've also converted into a class. However, I'm still seeing texture memory usage go up by about 1mb each time I play the game and this just continues happening every time I press play. So, all I can think now, is that it's true type font textures being retained. Will need to do some more work on this, I think.
  • john26john26 Maintainer
    I tried using collectgarbage("count") myself and found it gives very noisy results which vary from run to run. Furthermore the count value depends on whether you do print statements and such. I came to the conclusion that this value is not very helpful at detecting memory leaks. And it's just a total: no help in actually finding the leak.

    Some time ago, Atilim described a "secret" function that allows you to tell when a particular variable is collected.
    function MyClass:init()
       self.proxy = newproxy(true)
       getmetatable(self.proxy).__gc = function() print("collected!") end
    end
    :
    foo=MyClass.new()
    When foo gets collected, it prints the message "collected!". I found this to be much more useful as you can actually see when a particular object is collected.
    +1 -1 (+3 / -0 )Share on Facebook
  • @john26 Now that's really useful - cracking little piece of code which should make the problem easier to track down. Thank you.
  • Think I've got it. I'm using a unified broadcast system for events (such as game pause etc.) and, as part of this I'm using a table to register objects upon for this broadcast. Not setting allEventDispatchers[self] = nil when deleting an object means that a reference remains for it. Stupid error, completely ignored that as a possibility of it causing a problem. More work to do but I think this accounts for the problems.

    Likes: ar2rsawseen

    +1 -1 (+1 / -0 )Share on Facebook
  • Some notes that might help people:

    1. The code posted by @john26 is incredibly useful to track where a leak is happening. Use it on the higher up objects first to see if they're being collected correctly, gradually working your way down through the child classes until you find one that isn't being collected.

    2. Even using that, and even if you call collectgarbage() multiple times in a row, expect Lua to be lazy on actually collecting an object that's garbage. Don't expect to see the messages from the code from point 1 straight away, sometimes Lua likes to take its time.

    3. Don't expect XCode's Instruments to report leaks. It continually told me I had none when I actually did and lots of them. I don't know why this is, I can only presume that Lua obfusticates leaks from the XCode instruments. This is only based on what I've seen today, your mileage may vary.

    4. collectgarbage("count") and application:getTextureMemoryUsage() are useful. They're not always 100% accurate but they can definitely be used to track a trend. If they're both going up the longer your app is running then something is up. Just don't over-analyse the figures too much.

    5. If you're used to being in control of garbage collection, for instance if you're used to Objective-C, then prepare to give up a bit of control and let Lua do its thing. It does do it, just not as immediately as you might want it to.

    Anyway, the feedback on this thread has been really useful so thank you all that participated and I hope the cliff notes above help others (even if just a little bit)

    Likes: Platypus

    +1 -1 (+1 / -0 )Share on Facebook
  • Eh? If we can trust XCode instrument, then that is scary...

    Well, for me, i hope that Gideros can remove an object completely from the Application, not just remove it from stage, since watching memory rising up in the game scare me.
  • Eh? If we can trust XCode instrument, then that is scary...

    Well, for me, i hope that Gideros can remove an object completely from the Application, not just remove it from stage, since watching memory rising up in the game scare me.
    If memory is rising up considerably over time then you have a leak, simple as that. You remove it from the stage and Lua will then collect it as garbage, presuming that nothing references it any longer.

Sign In or Register to comment.