Start here. This is the direct spoken answer to practice first.
Overview
The collector can reclaim unreachable objects, but it cannot decide that a reachable object is no longer useful.
A Java memory leak usually means the application unintentionally keeps objects reachable after their useful lifetime. Common causes include unbounded caches, static collections, registered listeners that are never removed, queued work that never drains, and ThreadLocal values that outlive the request. The garbage collector is behaving correctly because a path from a GC root still reaches the objects. Fixing the leak means fixing that retaining reference or its ownership policy.