This example illustrates the use of PhantomReference objects to schedule
clean-up operations associated with an object.

The main method is in the "JarJarToy" class and simply loops creating instances
of that class.

Each instance creates an associated instance of "JarJarCleanupInfo" which
holds all of the state necessary for performing the clean-up of the
object.  In this case it's simply a number which is printed out in the
'doCleanup' method.  Note that "JarJarCleanupInfo" extends "PhantomReference"
and that the references are registered with a queue managed by the
"JarJarToyCleanupManager" class.

When the garbage collector detects that the JarJarToy object referred to
from a particular phantom reference has become unreachable it enqueues the
phantom reference object on the reference queue of the cleanup manager.  The
cleanup manager loops taking items from the reference queue and invoking
'doCleanup' on them.

This scheme is much more convoluted than simply adding a 'finalize' method
to the "JarJarToy" class.  However, it has several benefits:

 - it allows the programmer to decide exactly when the clean-up operations
   are performed, for example as here they could instantiate a separate thread
   for this purpose,
   
 - it avoids the problem of finalizers that 'resurrect' the object that they
   are invoked on by writing 'this' into a shared field -- with PhantomReferences
   it is the reference object rather than the JarJarToy object itself that is
   available whne running the clean-up code. 