You very rarely need to call gc(). It is not guaranteed to do anything. The JVM will usually work out when to do gc anyway.
To track the garbage collection of objects, you can use WeakReference objects. You may dedicate a thread to retrieving references from the queue associated with the WeakReferences and processing them accordingly. You may find it useful to subclass WeakReference, to store some additional information associated with the post-GC operation; if you do, be careful not to accidentally hard-reference the original object, or you will prevent it ever being GCd.
Also be aware of generational garbage collection, and the fact that the JVM implementation detail in this area changes from release to release. This is probably due to the importance in terms of performance.