The System.gc() will return when all objects have been scanned once.
An object should be finalized() AFTER it has been collected. Most objects don't implement this method but for the ones which do, they are added to a queue to be cleaned up later. This means those objects cannot be cleaned up yet (not the queue nodes which hold them) i.e. the act of triggering a GC can increase memory consumption temporarily.
Additionally there are SoftReferences to objects which may or may not be cleaned up by a GC. The assumption is these should only be cleaned up if not much else was cleaned up.
In short, not all objects can be cleaned up in one cycle.