"Hi, Amigo!"

"Hi, Rishi!"

"Well, how was your day?"

"Brilliant! Today Bilaabo told me about recursion, and Ellie told me about weak and soft references."

"Did she tell you about phantom references?"

"Are you talking about PhantomReference? She mentioned it, but didn't explain it in detail."

"Great, then I hope you won't mind if I fill this gap."

"Of course! I'll listen to you with pleasure, Rishi!"

"Great. Then I'll begin."

"Phantom references are the weakest references of all. They have effect only if an object doesn't have any references at all other than phantom references."

PhantomReference - 1

"A PhantomReference is used in a complex object deletion procedure. This may be necessary when an object does something outside of the Java machine, e.g. it calls low-level OS functions or writes its state to a file or does something else very important."

"Here's an example of how you might use it:"

Example of creating phantom references
// Special queue for phantom objects
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

// List of phantom references
ArrayList<PhantomReference<Integer>> list = new ArrayList<PhantomReference<Integer>>();

// Create 10 objects and add them to the list using phantom references
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
 list.add(new PhantomReference<Integer>(x, queue));
}

"I want to draw attention to the last line again. Not only is object x passed to the PhantomReference — a special queue of phantom references is as well."

"Why do we need this queue?"

"That's what I'm going to tell you now."

"When you destroy an object held by a phantom reference, it gets destroyed, but it isn't deleted from memory! What do you think about that?!"

"So, how does that work?"

"There are quite a few nuances here, so I'll start with the simplest."

"If only phantom references to an object remain, then this is what will happen to it:"

"Step 1. During the next garbage collection, the finalize() method will be called on the object. But, if the finalize() method hasn't been overridden, then this step is skipped, and step 2 is executed immediately."

"Step 2. During the next garbage collection, the object is placed in a special queue of phantom objects. It will be deleted from this queue when the clear() method is called on the PhantomReference."

"Who calls it? The object was deleted, right?"

"Well, the object did indeed die in our world (the Java world), but it hasn't disappeared. It remains as a phantom — the queue of phantom objects still holds a reference to it. The same ReferenceQueue whose reference we so carefully passed to the PhantomReference constructor."

"So this ReferenceQueue is like the afterlife?"

"More like a phantom world."

"And a phantom object can only be deleted by calling clear() on its phantom reference."

"Here's how to continue the previous example:"

Example of creating phantom references
// Special queue for phantom objects
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

// List of phantom references
ArrayList<PhantomReference<Integer>> list = new ArrayList<PhantomReference<Integer>>();

// Create 10 objects and add them to the list using phantom references
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
 list.add(new PhantomReference<Integer>(x, queue));
}

// Call the garbage collector and hope it will listen to us :)
// It should destroy all phantom reachable objects and put them in the queue
// of phantoms
System.gc();

// Get all objects from the queue
Reference<? extends Integer>referenceFromQueue;
while ((referenceFromQueue = queue.poll()) != null)
{
 // Display the object on the screen
 System.out.println(referenceFromQueue.get());

 // Clear the reference
 referenceFromQueue.clear();
}

"I understand that something is happening here. I almost even understand exactly what is happening."

"But how do you use this in practice?"

"Here's a better example:"

Example of creating phantom references
// Special queue for phantom objects
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

// List of phantom references
ArrayList<PhantomInteger> list = new ArrayList<PhantomInteger>();

// Create 10 objects and add them to the list using phantom references
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
 list.add(new PhantomInteger (x, queue));
}
This thread will monitor the phantom queue and remove objects from it
Thread referenceThread = new Thread()
{
 public void run()
 {
  while (true)
  {
   try
   {
    // Get the new object from the queue. If there is no object, then we wait!
    PhantomInteger ref = (PhantomInteger)queue.remove();
    // Call the close method on it
    ref.close();
    ref.clear();
   }
   catch (Exception ex)
   {
    // Write errors to a log
   }
  }
 }
};
// Run the thread as a daemon
referenceThread.setDaemon(true);
referenceThread.start();
This is a class that inherits PhantomReference and has a close() method
static class PhantomInteger extends PhantomReference<Integer>
{
 PhantomInteger(Integer referent, ReferenceQueue<? super Integer> queue)
 {
  super(referent, queue);
 }

 private void close()
 {
  System.out.println("Bad Integer totally destroyed!");
 }
}

"We did three things here."

"First, we created the PhantomInteger class, which inherits PhantomReference<Integer>."

"Second, this class has a special close() method. The need to call this method is what got all this started.

"Third, we declared a special thread: referenceThread. It waits in a loop until another object appears in the phantom queue. As soon as this happens, the thread removes the object from the phantom queue and calls its close() method. And then the clear() method. And that's it. The phantom can move on to a better world. It will no longer trouble us in ours."

"So interesting, but it all worked out."

"We're actually tracking a queue of dying objects, and then we can call a special method for each of them."

"But remember, you can't call the method on the object itself. You can't get a reference to it! The PhantomReference's get() method always returns null."

"But we inherit PhantomReference!"

"Even inside a subclass of PhantomReference, the get() method returns null."

"So I just save a reference to the object in the constructor"

"Ah. But such a reference would be a StrongReference, and the object will never end up in the phantom queue!"

"Dang. Okay, give up. If it's impossible, then it's impossible."

"Okay, good. I hope you've learned something valuable from today's lesson."

"Yes, there was so much new material. And I thought I already knew everything. Thank you for the lesson, Rishi."

"You're welcome. That's it, go relax. But don't forget — we have another lesson this evening."