Who am I? • Electrical & Electronics Engineer (2002) • MSc in Cryptography • 15 years in Defense Industry R&D • Since 2018 in Semiconductor Industry • Polyglot developer • 10+ years JVM • Father of two boys
Brain Waste Disposal System http://sitn.hms.harvard.edu/flash/2018/clearing-junk-healthy-lifestyle-choices-boost-brain-waste-disposal/ Pro Tip: Sleep is important!
In C and C++ freeing allocated memory is thought to be so important that it should be handled explicitly by the programmer using free/delete. the Garbage Collector” Charles Griffiths
In C# and Java freeing allocated memory is thought to be so important that it should be handled automatically using the Garbage Collector the Garbage Collector” Charles Griffiths
Why do we need Garbage Collection (GC)? • Moving responsibility of labor work from humans to machine • Automating memory allocation and deallocation • Preventing human errors • Minimizing possible memory leaks • Increasing developer productivity
Is there a productive and performant option? • Automatic reference counting (?) • When the reference count drops to zero the object is definitely unreachable and can be recycled
Swift Cyclic Reference ARC https://www.raywenderlich.com/966538-arc-and-memory-management-in-swift class User { let name: String private(set) var phones: [Phone] = [] func add(phone: Phone) { phones.append(phone) phone.owner = self } } class Phone { let model: String var owner: User? } func runScenario() { let user = User(name: "John") let iPhone = Phone(model: "iPhone Xs") user.add(phone: iPhone); } !!! Retain cycle problem 2 objects keep a reference to each other
Automatic Reference Counting • (+) Performant / deterministic compared to GC • (+) Can clean up entire object graphs • (+) Happens in the background no memory management effort needed • (+) Developer friendly, less prone to human errors • (-) Cannot cope with cycles • (-) Still some instructions from user required [Objective-C, Swift] [Java, C#] GC • (-) Can block application flow while doing GC • (-) Developer has no control over the scheduling, not deterministic • (-) Requires additional resources (memory, CPU) ARC vs GC
Productivity vs Performance • Manual memory managed systems are more performant • If everything is perfect and no human error • Trade off • General trend is towards productivity • JavaScript is # 1
Functional - Mark & Sweep • Proposed by Lisp inventor John McCarthy in 1960 • Each object has a ‘Mark Bit’ • Initially ‘0’ • Set to ‘1’ if reachable https://i.stack.imgur.com/W5pX6.gif
Evaluating a GC • Pause time vs productive time • Pause frequency • Pause distribution • Allocation performance • Concurrency • Scaling • CPU usage • Memory footprint
Stack • Used only by one thread of execution • Reclaimed when thread exits • Holds local primitive variables and references • LIFO access, easy to manage • java.lang.StackOverFlowError Stack vs Heap Heap • Used by the whole application, global memory • Reclaimed when GC kicks in or app exits • Holds actual object allocations • No enforced pattern, complex • java.lang.OutOfMemoryError
Generations Young & Old • Young Generation • Cheap to collect [Minor GC] • Old Generation • Long lived objects in Young Generation are ‘promoted’ to old generation • Typically larger than Young Generation • Expensive to collect [Major GC] Eden S1 S2 Old Young Generation Minor GC Major GC
Promotion Rate & Premature Promotion https://plumbr.io/blog/garbage-collection/what-is-promotion-rate • The faster objects promoted to old, the faster it gets full • Which means young is not big enough for new allocations • Once old is full enough -> Major Collection is triggered • Hence ‘Premature Promotion’ • Solution: Increase the size of young generation
Promotion Failure https://plumbr.io/blog/garbage-collection/what-is-promotion-rate • If old is full during a Minor GC? • To promote young objects, a minor GC is followed by a Full GC • Hence, ‘Promotion Failure’ • Solution: Increase the size of old generation
Young generation Eden getting full No effect on latency Major GC Old generation Minor GC fails Can have latency effect Full GC Whole heap + MetaSpace Minor or Major GC fail Can have latency effect
Serial GC (-XX:+UseSerialGC) • Stop the world & Serial • All GC work in a single thread • Pause times can be long • Grow more or less linearly with the heap size and amount of live data • Simplest & lowest memory footprint • Single processor, small data sets https://dzone.com/articles/choosing-the-right-gc
Parallel GC (-XX:+UseParallelGC) • STW & Parallel • Increased throughput • Default GC in JDK till Java 9 • Multithreaded old & young collectors • Memory tended to be 1GB – 2GB • Pause times increase with increased heap sizes • Does not support memory shrinking • Even after a Full GC, other processes will not be able to use available space • Not good for multi-tenant environments https://dzone.com/articles/choosing-the-right-gc
Concurrent Mark Sweep GC (-XX:+UseConcMarkSweepGC) • STW & Parallel Young Collection • Mostly Concurrent Old Collection • Good for lower worst-case pause times than Serial or Parallel GC • Reduced pause times • Sacrifices throughput • Tuning is hard concurrent work should be completed before out of memory • Requires 10-20% more heap space compared to Parallel GC • Compaction is not concurrent • Unpredictable DEPRECATED in Java 9 https://dzone.com/articles/choosing-the-right-gc
Traditional JVM GCs have some problems … • Necessity to scan whole OLD Generation • Mark, Sweep, Compact … • Huge heap → Huge latency • YOUNG and OLD are separate consecutive chunks of memory • Need to decide at JVM startup which one is which • Not flexible enough Eden S1 S2 Old Meta Young Generation
G1 GC (-XX:+UseG1GC) • STW & Parallel Young Collection • Concurrent mark reduces pauses significantly • Pauses are independent of heap size • Just set a pause time goal and let it work • Starts with the ‘dirtiest’ region first – Hence G1 • Has String Deduplication! • Compaction is still not concurrent https://dzone.com/articles/choosing-the-right-gc
Concurrency in Garbage Collection “With concurrent mark everything gets complicated: the application runs and actively mutates the object graph during the mark.” Alexey Shipilev
Tip: Choose the right GC • Need throughput => Choose Parallel GC • Need responsive app => Choose G1 or Shenandoah • Need multitenancy => Choose G1 or Shenandoah • Large heap: large live data sets ⇒ need concurrent GC • Slow hardware: memory is slow ⇒ need concurrent GC
Tip: Use Immutable Objects “Object whose fields cannot be modified(*) after the creation” - Fields should be created before the object itself - The class is always younger than its fields - When young collection, GC can skip scanning immutable objects in older generations. They cannot reference any younger objects. Eden S1 S2 Old https://blog.overops.com/5-tips-for-reducing-your-java-garbage-collection-overhead/
Tip: Limit the scope • If defined in scope & Out of scope -> eligible for garbage collection • Can be for, while, switch kind of statement scope • Can be method scope • Can be class scope • Chose smallest scope possible
Tip: Use Specialized Primitive Collections • int is 4 bytes • Integer is 16 bytes • 12 Object header + 4 int https://blog.overops.com/5-tips-for-reducing-your-java-garbage-collection-overhead/
Tip: Use static fields judiciously • Static fields are GC Roots! • Always reachable • Never eligible for garbage collection • One source for potential memory leaks in Java