Challenge 2: A Growing Static Collection as a Real Leak — Possible Solution ==================================================================== LeakyCache.java: import java.util.ArrayList; import java.util.List; public class LeakyCache { // A static field is reachable for as long as LeakyCache's class // itself is loaded -- effectively for the whole program's // lifetime. It is always a live GC root. private static final List cache = new ArrayList<>(); public void process(Object data) { cache.add(data); // added, never removed } // Why this is a genuine memory leak despite Java's garbage // collector: // // Every single object ever passed into process() becomes // reachable through `cache`, and stays reachable indefinitely, // because nothing ever calls cache.remove(...) or clears the // list. The GC's only question is "is this object reachable?" -- // and the answer here is permanently "yes," for every object ever // added, regardless of whether the program logically still needs // any of them. // // The GC cannot infer that these objects are logically stale or // unneeded -- it has no concept of "business logic no longer // cares about this." As far as it's concerned, cache genuinely // might still need every element it holds, so it correctly (by // its own rules) never collects any of them. The list simply // grows without bound for as long as the program runs, consuming // more and more heap memory -- a real, classic leak, just one // caused by an unintentionally-retained reference rather than a // forgotten free() call. } WHY THIS WORKS AS AN ANSWER ------------------------------ This constructs the exact "ever-growing static collection" scenario the chapter's warn-box names, and the explanation correctly distinguishes reachability (which the GC can determine) from logical need (which it cannot), matching the chapter's own stated distinction.