What are weak, soft and phantom references in Java, and how do they differ?
All three are ways of holding a reference to an object without preventing the garbage collector from reclaiming it. The difference is how eagerly the collector is allowed to reclaim the referent.
A weak reference is cleared as soon as the object is only weakly reachable — that is, as soon as no strong reference remains. There is no guarantee about when it happens, but it happens promptly, because a weak reference is only useful for something you are happy to lose. The canonical use is a cache keyed by an object you do not control, where losing the entry is fine but leaking it is not.
A soft reference is cleared only when the collector is running out of memory. That makes it a gentler cache: the entries survive as long as there is room for them, and are dropped under pressure. The catch is that the timing is not deterministic, so you still need an eviction policy for a bounded cache — a soft reference makes the cache self-limiting under pressure but does not make it bounded.
A phantom reference is never cleared in the useful sense. The referent stays allocated until the phantom reference is enqueued, and get always returns null. It exists for one job: knowing when an object has been determined to be unreachable, which is the closest a Java program can get to a finalisation hook that actually runs reliably. The ReferenceQueue is what tells you the object is gone.
The rule of thumb: weak for a cache you can afford to lose, soft for a cache you want to keep if possible, phantom for post-mortem cleanup such as native resources.