Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › JVM

GC and memory leaks

Who reclaims discarded objects, and when?

Garbage collection (GC) is the JVM automatically reclaiming the memory of objects that are no longer used. New objects are created in eden, and when eden fills up, a minor GC picks out only the live objects and moves them. Objects that survive several times are promoted to the old generation, and when the old generation fills up, a full GC runs, which takes much longer. A memory leak is the buildup of objects that are no longer needed but cannot be collected because something still refers to them, and this lab also shows what happens to the heap when there is a leak.

Partly checked against real execution · Only the direction was checked on JDK 21 (the numbers are from the model)

Concepts in this lab

Garbage collection (GC)
This JVM feature finds objects that the program no longer uses and reclaims their memory automatically. Developers do not have to free memory themselves, which prevents errors caused by mistakes in freeing it. In return, the application may pause briefly during a collection, and objects that are still referenced cannot be reclaimed.
Reachability and GC roots
The GC decides whether an object is still in use by asking "is it reachable?". An object that can be reached by following references from starting points that are always alive (GC roots), such as the local variables of running threads and static variables, is treated as live, and an object that cannot be reached is treated as a candidate for reclamation.
Young generation and old generation
This structure divides the heap based on the observation that most objects become useless soon after they are created. New objects are created in the young generation (in eden), and objects that survive several collections of the young generation are moved to the old generation. Collecting only the young generation often, where short-lived objects gather, reclaims a lot of memory at low cost.
Minor GC and full GC
A minor GC (young GC) collects only the young generation; it happens often but usually finishes quickly. A full GC collects the whole heap; it takes a long time and also pauses the application for longer. If full GCs happen often, or memory use does not go down even after a full GC, suspect a memory leak or a heap that is too small.
Stop-the-world (STW)
This means pausing all threads of the application so that garbage collection can be done safely. Request handling stops during this time, so it shows up as response delays. Recent collectors (G1, ZGC) are designed with the goal of keeping these pauses short.
Memory leak
This is a state in which objects that are no longer needed are still referenced from somewhere, so the garbage collector cannot reclaim them. Memory use grows over time and eventually leads to an OutOfMemoryError. Typical causes are data that keeps piling up in a static collection, a cache with no expiration, and ThreadLocal values that are not cleaned up in a thread pool.
StackOverflowError and OutOfMemoryError
A StackOverflowError occurs when method calls go so deep that they exceed the thread's stack space, and the usual cause is recursion with a wrong termination condition. An OutOfMemoryError occurs when memory such as the heap runs short and no more objects can be allocated, and common causes are a memory leak or loading too much data at once.

Try these

Running with the default settings

  1. Tick 1: 5 objects are created. Eden: 5/20 slots in use.
  2. Tick 2: 5 objects are created. Eden: 10/20 slots in use.
  3. Tick 3: 5 objects are created. Eden: 15/20 slots in use.
  4. Tick 4: 5 objects are created. Eden: 20/20 slots in use.
  5. Tick 5: Eden is full. A minor GC examines only the young generation (20 objects). It discards the objects that are not referenced (18) and moves the live objects (2). Moved to the survivor space: 2. Promoted to the old generation: 0. Eden is now empty. The application pauses briefly (a pause of 1).
  6. Tick 5: 5 objects are created. Eden: 5/20 slots in use.
  7. Tick 6: 5 objects are created. Eden: 10/20 slots in use.
  8. Tick 7: 5 objects are created. Eden: 15/20 slots in use.
  9. Tick 8: 5 objects are created. Eden: 20/20 slots in use.
  10. Tick 9: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  11. Tick 9: 5 objects are created. Eden: 5/20 slots in use.
  12. Tick 10: 5 objects are created. Eden: 10/20 slots in use.
  13. Tick 11: 5 objects are created. Eden: 15/20 slots in use.
  14. Tick 12: 5 objects are created. Eden: 20/20 slots in use.
  15. Tick 13: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  16. Tick 13: 5 objects are created. Eden: 5/20 slots in use.
  17. Tick 14: 5 objects are created. Eden: 10/20 slots in use.
  18. Tick 15: 5 objects are created. Eden: 15/20 slots in use.
  19. Tick 16: 5 objects are created. Eden: 20/20 slots in use.
  20. Tick 17: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  21. Tick 17: 5 objects are created. Eden: 5/20 slots in use.
  22. Tick 18: 5 objects are created. Eden: 10/20 slots in use.
  23. Tick 19: 5 objects are created. Eden: 15/20 slots in use.
  24. Tick 20: 5 objects are created. Eden: 20/20 slots in use.
  25. Tick 21: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  26. Tick 21: 5 objects are created. Eden: 5/20 slots in use.
  27. Tick 22: 5 objects are created. Eden: 10/20 slots in use.
  28. Tick 23: 5 objects are created. Eden: 15/20 slots in use.
  29. Tick 24: 5 objects are created. Eden: 20/20 slots in use.
  30. Tick 25: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  31. Tick 25: 5 objects are created. Eden: 5/20 slots in use.
  32. Tick 26: 5 objects are created. Eden: 10/20 slots in use.
  33. Tick 27: 5 objects are created. Eden: 15/20 slots in use.
  34. Tick 28: 5 objects are created. Eden: 20/20 slots in use.
  35. Tick 29: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  36. Tick 29: 5 objects are created. Eden: 5/20 slots in use.
  37. Tick 30: 5 objects are created. Eden: 10/20 slots in use.
  38. Tick 31: 5 objects are created. Eden: 15/20 slots in use.
  39. Tick 32: 5 objects are created. Eden: 20/20 slots in use.
  40. Tick 33: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  41. Tick 33: 5 objects are created. Eden: 5/20 slots in use.
  42. Tick 34: 5 objects are created. Eden: 10/20 slots in use.
  43. Tick 35: 5 objects are created. Eden: 15/20 slots in use.
  44. Tick 36: 5 objects are created. Eden: 20/20 slots in use.
  45. Tick 37: Eden is full. A minor GC examines only the young generation (22 objects). It discards the objects that are not referenced (18) and moves the live objects (4). Moved to the survivor space: 2. Promoted to the old generation: 2. Eden is now empty. The application pauses briefly (a pause of 1).
  46. Tick 37: 5 objects are created. Eden: 5/20 slots in use.
  47. Tick 38: 5 objects are created. Eden: 10/20 slots in use.
  48. Tick 39: 5 objects are created. Eden: 15/20 slots in use.
  49. Tick 40: 5 objects are created. Eden: 20/20 slots in use.

Result of this run

Code

// Heap: eden 20 slots, survivor 5 slots, old 40 slots

while (running) {                       // one pass = one tick
    for (int i = 0; i < 5; i++) {
        Event e = new Event();           // created in eden
        if (keepForAWhile(e)) {          // 10%
            recent.put(e.id(), e);       // removed 10 ticks later
        }
    }                                    // the rest are discarded here
}

Assumptions of this model

This is a simple model of generational garbage collection in which all objects have the same size and the heap is counted in slots. The condition for promotion is lowered to "survives two minor GCs" (the real default maximum is 15, and the garbage collector adjusts it to the situation). The pause times 1 and 10 only express the relationship that a minor GC is much shorter than a full GC. Real collectors such as G1 and ZGC work differently, for example by dividing the heap into smaller parts and working concurrently with the application, but the principle is the same: the area for new objects and the area for old objects are handled separately, and objects that are still referenced cannot be reclaimed. The slot counts and GC counts are values of the model. However, the same situations were actually run on JDK 21 on this PC with a 64MB heap to check the direction. Creating only objects that are discarded quickly gave 24 young collections and 0 full GCs. Mixing in objects that are held for a long time gave 7 full GCs (average pause: young 2.1ms, full 11.5ms). Continually adding objects to a list that is never cleared ended with an OutOfMemoryError after 37 repeated full GCs. With the heap enlarged to 128MB, the same leak ended with the same error after creating about twice as many objects.

Previous lab: Stack and heap · Next lab: JIT warmup

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these