The system internally keeps a count of the number of references that are currently active for a given object. When a new reference is added, that reference counter is incremented by 1. Conversely when a reference is removed it's decremented. Once that internal reference count reaches 0, an object is eligible for garbage collection.
Corey's flash animation is a great tool, and his suggestion of "drawing lines" for each reference will help you diagram out what's going on.
A couple of more bits of information related to Garbage Collection (some on the test, some just nice to know).
- Garbage Collection algorithms are largely left up to the VM implementer. After an object is marked available for collection there is no definite way to tell when it is actually going to be collected.
- According to the spec, calling System.gc() only SUGGESTS that system expend effort performing garbage collection. It does NOT guarantee it. Again, this is an implementation detail left up to the VM creator.
- Garbage collection runs as a lower priority thread than your applications do. If you have a very busy system, the GC may not run for a long time. I've actually seen this on some heavily loaded servlet based systems where data piles up for hours before GC finally gets a chance to kick in.
- The garbage collector will not collect an object until it's finalization is completed. It's better to clean early than rely on finalize for cleanup. Otherwise it may take more than one pass of the GC thread to reclaim the memory that was allocated.
For more insights into GC and finalization, check out the discussion in the
JLS Macon