Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reasons why garbage collector takes all cpu time on each frame

Some times my adobe air application becomes very slow because garbage collector starts to work continiosly on each frame and takes more then 800% of budget. it lasts several minutes or even more. This problem appears only on iPhone 4/4s from time to time and after restarting device everything works correct for some time.

Maybe somebody also has such situation and know the ways how to prevent it?

UPDATE: object pools and other allocation-prevention approaches already implemented. So there's nothing really to collect and GC is just spending CPU for nothing. When this ends memory usage is still the same. Also it does not happen every time even if same input and scenario is used. So I suppose there's some 'unlucky' situation with allocated heap being around some threshold where AIR decides to make clean up before taking another chunk from the system. Then it found few objects to dispose and no new chunk is required again. On next frame few objects are created (very few) and scenario is repeated.

Profiler screenshot

like image 200
Mishganches Avatar asked Feb 20 '14 08:02

Mishganches


1 Answers

A few points to keep in mind:

  • Garbage collection only kicks in when your app requests memory; Flash will try to recover some memory from your app before requesting another memory page (an allocation of around about 1MB) from the OS. Because your garbage collection is kicking in quite often, it suggests that your app is taking up more memory than you think
  • The GC works based on references: var s:Sprite = new Sprite() creates a reference to a Sprite. var s2:Sprite = s creates another reference. For the memory that your Sprite takes to be considered for garbage collection, both s and s2 need to be set to null. If they're local variables, this will happen automatically when they go out of scope (e.g. if they're declared in a function, at the end of the function)
  • Once all references to an object are cleared, the allocated memory can be recovered. Sometimes, however, you can have a problem with circular references - where 2 objects hold a reference to the other, but nothing has a reference to them
  • To get around this, the GC has 2 phases, generally speaking: the mark phase, and the sweep phase. The mark phase will start at the root of your app (the stage), and try and touch every object that it can. Everything that's not touched is assumed to be dead and can be collected. All "dead" objects are cleaned up in the sweep phase.
  • The mark phase is super slow, so you want to try and avoid it as much as possible, by essentially keeping your memory use static.

To help the GC:

  • Clear your event listeners, or use weak listeners (which don't add references)
  • Clear your explicit references - implement a destroy() or a dispose() method that you call when you want to get rid of something, and null everything that's an object
  • As mentioned, reused objects using an object pool
  • Some objects, like BitmapData and XML can be immediately destroyed (BitmapData.dispose() and System.disposeXML()). If it's only used for setup, or can be reloaded, get rid of it. NOTE: if you're using Starling, and you create Textures from Bitmaps, then you probably don't need the original, unless you want to deal with lost contexts, but you can probably just load everything back in.
  • Be careful of hidden allocations: cacheAsBitmap is allocating a bitmap in the background, ditto for filters; Array.splice() is creating a new array etc
  • Watch out for any object creation that happens in loops etc

I wrote a class ages ago to help track memory leaks (http://divillysausages.com/blog/tracking_memory_leaks_in_as3), but you should be able to get even more in-depth using Scout - take a memory snapshot, play around a bit, then take another one; you can compare the two and highlight any created objects, including (I think) where they're being created.

Another point to keep in mind is if your device actually has enough memory to run your app in the first place - e.g. if your app (assuming everything is optimised etc) takes 100MB of memory, but you only have 80MB available, the GC will be continuously running to try and allocate the last 20

like image 109
divillysausages Avatar answered Sep 28 '22 03:09

divillysausages