Garbage truck at work. Cleaning up and recycling.

ART’s Garbage Collector: Strategies for best performance

How Android RunTime clean up memory while reducing impact on performance by leveraging multiple strategies.

Cedric Ferry
4 min readSep 3, 2024

--

Introduction

In the previous articles (1, 2) we learned about memory leaks, how they occur, how to prevent them with de-referencing and weak references.

Today we are going to explore how Android RunTime’s Garbage Collector improves from Dalvik and how the algorithms works.

Definitions

Android RunTime: also called ART, is an application runtime environment used by the Android operating system. ART performs the translation of the application’s bytecode into native instructions that are later executed by the device’s runtime environment. — Wikipedia

Dalvik: is a discontinued process virtual machine (VM) in the Android operating system that executes applications written for Android — Wikipedia

Garbage Collector: or GC, is a form of automatic memory management, it attempts to reclaim memory that was allocated by the program, but is no longer referenced — Wikipedia

Android RunTime improvements over Dalvik

Android RunTime’s garbage collector has significantly improved performance compared to its predecessor, Dalvik (about 2x with its initial release 10 years ago!). ART implements a generational approach and concurrent copying. these techniques help reduce the frequency and duration of GC pauses, which are responsible for performance degradation. Google continue to deliver performance improvements to ART for every Android release.

The result is apps experience fewer jank and interruptions due to garbage collection.

Additionally, ART’s efficient memory management helps to optimize overall system performance by reducing memory fragmentation and improving application responsiveness.

However, excessive object creation and bad memory management can still negatively impact performance. Typically memory leaks encourage the GC to trigger more often. You should try to optimize your code for efficient memory usage, de-reference object when possible and avoid unnecessary object allocations.

Let’s have a closer look to these strategies and how they allow better performance compare to older Dalvik.

What is the “generational approach”

The generational approach is a strategy that divides objects into different generations based on their age. New objects are placed in a young generation memory space, and as they survive multiple garbage collection cycles, they are promoted to older generations. This approach is based on the observation that most objects are short-lived and can be reclaimed quickly.
By dividing objects into generations, the GC can optimize its collection efforts. It can focus on the young generation, where most objects are likely to be garbage, and use more aggressive techniques to reclaim them. For older generations, which contain long-lived objects, the collector can employ less frequent and less aggressive collection cycles.

Animation of the generational approach. some object get garbage collected, then as they age, they get promoted to the older generation that the GC check less often.

The trade off is, the generational approach, consumes slightly more memory to store objects in different generations. It can also introduce pauses during garbage collection when an older object needs to be reclaimed.

This generational approach results in improved performance and reduced overhead, making the garbage collection process more efficient.

How concurrent copy works

Concurrent copy works by copying live objects from the old generation to a new, empty region of memory while the application continues to run. This process is performed concurrently, meaning that the garbage collector can work in parallel with the application threads, minimizing the impact on user experience.

The concurrent marking operation which marks live objects in the old generation runs on a separate thread and on a separate CPU-Core, which prevents GC pauses described earlier.

The concurrent sweeping operation works the same way and reclaim unused object memory, since it runs in a separate thread, the impact on performance is minimal.

Note that concurrent copying reduces memory fragmentation but also consumes slightly more memory to handle the young and old object space.

How ART reduces memory fragmentation

ART’s garbage collector also prevents memory fragmentation. The memory fragmentation can happen when objects are allocated then freed at different places. This creates non-contiguous memory blocks which take longer to read as the system. It also forces the system to allocate in non-contiguous memory blocks.

ART sorted this issue by implementing “compaction” to defragment the memory.

Compaction rearranges the memory blocks so that they are contiguous.

Do you remember Window 95 Defrag tool? it works kinda the same.

Non fragmented memory improves memory allocation and reduces memory access time.

Conclusion

Today we learn how the garbage collector leverage multiple strategies to prevent blocking the app while running. ART improves efficiency and reduces impact on performance. I hope this article series can help Android Developers better understand how to optimize for memory leaks prevention.

If you read this far, please consider clapping 👏🏼 to support the author, thank you 🙏🏼

This article is sponsored by Android Developer News, app is available on the playstore.

--

--