Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dealing with GC on Android

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Dealing with GC on Android

Garbage Collection on Android is the number 1 killer of performance. In this talk we'll see how we can deal with it.

Avatar for Jason Chionh

Jason Chionh

April 02, 2014

Other Decks in Programming

Transcript

  1. What is it? • A feature of Java. • You

    can simply “new” stuff let the system clean up after you.
  2. What is it? • GC stops everything else when it

    is cleaning up. • This means your program will pause for the duration of GC. • Say goodbye to smooth UI animations and responsive game/app experience.
  3. How much $? • Convenience is great. • But only

    in situations where you can afford to pay for it. • Code readability and maintainability is great. • You can still have readability and maintainability AND avoid paying for GCs. • That's the craft of Software Engineering.
  4. GC for allocation • GC_FOR_ALLOC • When there's not enough

    free memory in the heap to allocate a new object. • Usually due to fragmentation. • Wait, what's fragmentation?
  5. Fragmentation 1. Do a GC 2. Can fit? Nope. 3.

    Grow heap and fit. 4. No compaction step.
  6. GC for allocation (frag) • We want to allocate 35k,

    memory is fragmented, can't allocate. • Pay the expensive cost of a GC and freed ~600k • Now we've got ~1.4Mb free. • Still can't allocate 35k! • Grow Heap and allocate.
  7. Concurrent GC • GC_CONCURRENT • Run when the heap is

    almost full. • Hope that by the time GC completes, there's enough space for future allocations. • Runs concurrent, does not block the UI thread. • Effective? • Remember: Dalvik does not compact RAM. • High chance that GC_FOR_ALLOC is coming, due to fragmentation.
  8. Concurrent GC • But it does not block UI Thread,

    so we've got UI fluidity! • Hello, meet WAIT_FOR_CONCURRENT_GC • When there's an allocation, you'll be blocked.
  9. Detect it • Logs: DEBUG/dalvikvm(23500): GC_FOR_ALLOC freed 592K, 7% free

    20603K/22052K, paused 66ms, total 66ms INFO/dalvikvm-heap(23500): Grow heap (frag case) to 20.186MB for 35398- byte allocation DEBUG/dalvikvm(30467): GC_CONCURRENT freed 1207K, 6% free 20644K/21948K, paused 1ms+33ms, total 88ms DEBUG/dalvikvm(2747): WAIT_FOR_CONCURRENT_GC blocked 113ms DEBUG/dalvikvm(759): GC_EXPLICIT freed 1201K, 22% free 51344K/65264K, paused 2ms+8ms, total 108ms
  10. Detect it • Graph it out. • Measure time between

    onDraw() calls. • 03-10 15:30:41.614: DEBUG/dalvikvm(5765): GC_FOR_ALLOC 03-10 15:30:41.634: DEBUG/dalvikvm(5765): GC_CONCURRENT 03-10 15:31:13.474: DEBUG/dalvikvm(5132): GC_CONCURRENT 03-10 15:31:41.674: DEBUG/dalvikvm(5765): GC_CONCURRENT 03-10 15:31:41.674: DEBUG/dalvikvm(5765): WAIT_FOR_CONCURRENT_GC blocked 03-10 15:31:59.834: DEBUG/dalvikvm(1021): GC_CONCURRENT 03-10 15:32:41.714: DEBUG/dalvikvm(5765): GC_FOR_ALLOC 03-10 15:32:41.724: DEBUG/dalvikvm(5765): GC_FOR_ALLOC 03-10 15:32:41.774: DEBUG/dalvikvm(5765): GC_CONCURREN
  11. Prevent it • The more objects you allocate, the longer

    the GC pauses. • Dalvik's GC is Mark and Sweep Root g f h d e c b a
  12. Prevent it • Do not abuse the “new” operator. •

    The more “new”s you have, the more places GC can pause your program. • The more it causes memory fragmentation, which greatly increases chance of a GC. • Premature optimization is the root of all evil?
  13. Death by a Million Cuts (tm)! • A little “new”

    here won't hurt anyone.... • This is a global code style issue, and it will be EVERYWHERE in the codebase. • You can't just single out one module, measure that, and optimize when things slow down.
  14. Death by a Million Cuts (tm)! • All the “new”s

    everywhere in the code path contributes to the GC problem. • When you realize the GC issue, you might need to re-write your entire codebase • And then you give up and just live with what you got. • Death by a Million Cuts (tm).
  15. Work around it • Ok, so you're suffering from “Death

    by a Million Cuts (tm)” What can you do? • GC pauses allocations from the SAME HEAP where the GC is operating on. • Different processes uses different heaps. • Separate the abusive code to another process. • Those network calls, those database queries.
  16. Different process • Is your code modular that they can

    run as a service, content provider, broadcast receiver, or different activities? • Yes? Then split them up accordingly. • AndroidManifest.xml • android:process=”:process_name”
  17. Process • Processes will have to communicate via inter-process communication

    (IPC) • Via Intents and bundles, Binders and Messengers. • Data will have to be copied over through IPC, there will be allocations, unfortunately :( • At least, separate UI / GL render heavy activities to their own processes. • So that GCs on network or database don't block UI.
  18. Work around it • Pre-allocate if you can, outside of

    hot code path. • Object pooling. • Comes with its own set of problems in multi- threaded code. • The only winning move is to not play. :( • (Emscripten, asm.js and Unreal)
  19. Conclusion • GC hurts your game/app performance. • Memory fragmentation.

    • The more you allocate, the more GCs and the longer the GC pauses. • Don't abuse the “new”. • Counter-intuitive to Java that asks you to ignore memory, when you can't afford to. • Separate the heaps by running on separate processes.
  20. Garbage Collection rant • You are asking the programmer to

    stick his/her head in the sand like an ostrich and completely ignore the elephant in the room happily tearing up your app's performance and fluidity. • That elephant in the room is MEMORY. • Java tries to hide it from you, however, Don't ignore MEMORY.