Topic 6. Memory and Garbage Collector
Memory behavior in Java is a core reliability topic. Many production failures are caused by reference lifetime mistakes, not syntax issues.
Basic memory model
stack and heap have different responsibilities:
stackstores method frames, local variables, and references,heapstores objects and arrays,- method frame disappears after method completion,
- heap object stays alive while reachable.
int age = 30; // primitive value
String name = "Alex"; // reference to heap object
How Garbage Collector works
GC uses reachability analysis, not timer-based cleanup.
Simplified flow:
- JVM identifies
GC Roots(active threads, stack references, static fields, etc.). - Reachable objects are marked alive.
- Unreachable objects become reclaim candidates.
- Memory may be compacted to reduce fragmentation.
Key point: GC timing is nondeterministic. System.gc() is only a hint.
Generational memory model
Most JVM collectors rely on generations:
Young Generation: new objects,Old Generation: long-lived objects,Metaspace: class metadata.
Why it helps: many objects are short-lived, so frequent cheap young collections are effective.
GC pauses in practical terms
Minor GC: more frequent, typically shorter, mostly young generation.Major/Full GC: less frequent, usually heavier, may involve old generation.
If service latency spikes, long or frequent GC pauses are common suspects.
Typical Java memory leak patterns
A Java memory leak usually means “objects remain reachable accidentally”, not “GC is broken.”
Common causes:
- unbounded
staticcollections, - cache without eviction policy,
- event listeners/subscriptions never removed,
- uncleared
ThreadLocalin long-lived threads, - heavyweight object graphs pinned by singleton components.
Example:
public class CacheHolder {
private static final List<byte[]> CACHE = new ArrayList<>();
public static void add(byte[] data) {
CACHE.add(data); // unbounded growth
}
}
Objects remain reachable through CACHE, so GC cannot reclaim them.
Writing GC-friendly code
- bound cache and queue sizes,
- release subscriptions/resources explicitly,
- avoid unnecessary long-lived references,
- reduce temporary allocations in hot loops,
- plan lifecycle for large buffers.
Practical diagnostics workflow
- Monitor heap usage and GC pause metrics.
- Capture heap dump when memory keeps rising.
- Use dominator tree to find who retains memory.
- Correlate memory growth with workload patterns.
Key takeaway
- GC removes only unreachable objects.
- Most memory issues are reference-graph design issues.
- Memory management quality starts at architecture level.