|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 6 of 7
To obtain that notification, your code first creates WeakReference and ReferenceQueue objects. The code then passes Employee and ReferenceQueue object references to the WeakReference constructor. Next, the program stores the WeakReference object and a newly created Benefits object in a hash table data structure. That data structure forms an association between the WeakReference object (known as the key, because it identifies the hash table entry) and the Benefits object (known as that entry's value). The program can enter a polling loop where it keeps checking the reference queue to find out when the garbage collector
clears the weak reference to the Employee object. Once that happens, the garbage collector places a reference to the WeakReference object on the reference queue. The program can then use that reference to remove the WeakReference/Benefits entry from the hash table. To see how the program accomplishes that task, check out Listing 2; it creates a String object as the key and an Object object as the value:
Listing 2. WeakReferenceDemo.java
// WeakReferenceDemo.java
import java.lang.ref.*;
import java.util.*;
class WeakReferenceDemo
{
public static void main (String [] args)
{
// Create a String object that is strongly reachable from key.
String key = new String ("key");
/*
Note: For this program, you cannot say String key = "key";. You
cannot do that because (by itself), "key" is strongly
referenced from an internal constant pool data structure
(that I will discuss in a future article). There is no
way for the program to nullify that strong reference. As
a result, that object will never be garbage collected,
and the polling loop will be infinite.
*/
// Create a ReferenceQueue object that is strongly reachable from q.
ReferenceQueue q = new ReferenceQueue ();
// Create a WeakReference object that is strongly reachable from wr.
// The WeakReference object encapsulates the String object that is
// referenced by key (so the String object is weakly-reachable from
// the WeakReference object), and associates the ReferenceQueue
// object, referenced by q, with the WeakReference object.
WeakReference wr = new WeakReference (key, q);
// Create an Object object that is strongly reachable from value.
Object value = new Object ();
// Create a Hashtable object that is strongly reachable from ht.
Hashtable ht = new Hashtable ();
// Place the WeakReference and Object objects in the hash table.
ht.put (wr, value);
// Remove the only strong reference to the String object.
key = null;
// Poll reference queue until WeakReference object arrives.
Reference r;
while ((r = q.poll ()) == null)
{
System.out.println ("Polling reference queue");
// Suggest that the garbage collector should run.
System.gc ();
}
// Using strong reference to the Reference object, remove the entry
// from the Hashtable where the WeakReference object serves as that
// entry's key.
value = ht.remove (r);
// Remove the strong reference to the Object object, so that object
// is eligible for garbage collection. Although not necessary in this
// program, because we are about to exit, imagine a continuously-
// running program and that this code is in some kind of long-lasting
// loop.
value = null;
}
}
Unlike SoftReferenceDemo, WeakReferenceDemo doesn't spend much time polling. After one call to System.gc ();, the garbage collector clears the weak reference to the String referent in the WeakReference object that wr references. Basically, WeakReferenceDemo works as follows:
WeakReferenceDemo creates a String object that serves as a key. You must create that String object with a String constructor instead of simply assigning a String literal to key. The garbage collector will probably never collect String objects that you -- apparently -- create by assigning string literals to String reference variables. (A future article exploring strings will explain why.)
String's creation, WeakReferenceDemo creates a ReferenceQueue object and a WeakReference object that encapsulates String, which becomes the referent. We now have strong (via key) and weak (via the weak reference in the WeakReference object that wr references) references to String.
WeakReference creates an Object value object that eventually associates with the String key object. A Hashtable object is created, and the table stores an entry consisting of the objects WeakReference and Object. (You will learn about the Hashtable data structure class in a future article.)
WeakReference nullifies the String object's key reference, so the only String reference is the weak reference inside the WeakReference object.
WeakReference object on the reference queue. On my platform, only one System.gc (); call is necessary to request the garbage collector to run. The first time it runs, all weak references clear, and the next
call to q.poll () returns a reference to WeakReference.
WeakReference/Object entry from the hash table and nullify the Object's reference in value.
| Note |
|---|
To save yourself the trouble of automatically removing entries from a hash table, Java supplies the WeakHashMap class. Investigate that class in the SDK documentation and rewrite WeakReferenceDemo to use that class.
|
The phantomly reachable state manifests itself in Java through the PhantomReference class. When you initialize a PhantomReference object, you store a referent's reference in that object. The object contains a phantom reference to the referent, and the referent is phantomly reachable if there are no other references, apart from phantom references,
to that referent. A phantomly reachable referent's finalize() method (assuming that method exists) has already run and the garbage collector is about to reclaim the referent's memory.
A program receives notification, by way of a reference queue, when the referent becomes phantomly reachable, and subsequently
performs post-finalization cleanup tasks that relate to the referent but do not involve the referent -- because there is no
way for the program to access the referent.
Unlike SoftReference and WeakReference objects, you must create PhantomReference objects with a reference queue. When the garbage collector discovers that a PhantomReference object's referent is phantomly reachable, it appends a PhantomReference reference to the associated reference queue -- by calling Reference's enqueue() method. The following code fragment demonstrates the creation of a PhantomReference containing a String referent:
ReferenceQueue q = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (new String ("Test"), q);
There is a second difference between SoftReference, WeakReference, and PhantomReference objects. If either a SoftReference or a WeakReference object has an associated reference queue, the garbage collector places a reference to that object on the reference queue
sometime after it clears the referent's soft or weak reference. In contrast, the garbage collector places a reference to a
PhantomReference object onto the queue before the phantom reference clears. Also, the program, not the garbage collector, clears the phantom reference by calling PhantomReference's clear() method, inherited from the Reference class. Until the phantom reference clears, the garbage collector does not reclaim the referent. Once the phantom reference
clears, however, the referent moves from the phantomly reachable state to the unreachable state. Game over for the referent!
| Note |
|---|
By now, you know a call to get() on a SoftReference or WeakReference object, whose reference returns from a call to poll() or remove(), cannot return a referent's reference. That's not possible because the garbage collector clears the referent's soft or weak
reference before placing the SoftReference or WeakReference object's reference on the queue. However, because the garbage collector does not clear the referent's phantom reference,
you would expect get() to return a reference to that referent. Instead, get() returns null to prevent the referent from being resurrected.
|
Listing 3 demonstrates phantom references:
Listing 3. PhantomReferenceDemo.java
// PhantomReferenceDemo.java
import java.lang.ref.*;
class Employee
{
private String name;
Employee (String name)
{
this.name = name;
}
public void finalize () throws Throwable
{
System.out.println ("finalizing " + name);
super.finalize ();
}
}
class PhantomReferenceDemo
{
public static void main (String [] args)
{
// Create an Employee object that is strongly reachable from e.
Employee e = new Employee ("John Doe");
// Create a ReferenceQueue object that is strongly reachable from q.
ReferenceQueue q = new ReferenceQueue ();
// Create a PhantomReference object that is strongly reachable from
// pr. The PhantomReference object encapsulates the Employee object
// (so the Employee object is phantomly reachable from the
// PhantomReference object), and associates the ReferenceQueue object,
// referenced by q, with the PhantomReference object.
PhantomReference pr = new PhantomReference (e, q);
// Remove the only strong reference to the Employee object.
e = null;
// Poll reference queue until PhantomReference object arrives.
Reference r;
while ((r = q.poll ()) == null)
{
System.out.println ("Polling reference queue");
// Suggest that the garbage collector should run.
System.gc ();
}
System.out.println ("Employee referent in phantom-reachable state.");
// Clear the PhantomReference object's phantom reference, so that
// the Employee referent enters the unreachable state.
pr.clear ();
// Clear the strong reference to the PhantomReference object, so the
// PhantomReference object is eligible for garbage collection. (The
// same could be done for the ReferenceQueue and Reference objects --
// referenced by q and r, respectively.) Although not necessary in
// this trivial program, you might consider doing such clearing in a
// long-running loop, so that objects not needed can be collected.
pr = null;
}
}
When run, PhantomReferenceDemo produces output similar to the following:
Polling reference queue finalizing John Doe Polling reference queue Employee referent in phantom-reachable state.
The garbage collector has not yet run when the first Polling reference queue message appears. The first call to System.gc (); causes the JVM to try to run the garbage collector. It runs and executes Employee's finalize() method, which prints finalizing John Doe. The second Polling reference queue message indicates that a second call is made to System.gc ();. That call causes the garbage collector to move the Employee referent from the resurrectable state to the phantomly reachable state.
A close look at Listing 3 shows a pr.clear (); method call. That method call clears the phantom reference to the Employee referent in the PhantomReference object. That referent now enters the unreachable state, and the garbage collector can reclaim its memory the next time it
runs.
The Reference Objects API gives your programs limited interaction with the garbage collector through the SoftReference, WeakReference, and PhantomReference classes. Objects created from SoftReference contain soft references to their referents. You can use soft references to manage image and other memory-sensitive caches.
Objects that you create from WeakReference contain weak references to their referents. You use weak references to obtain notification when significant objects are no
longer strongly reachable. Finally, PhantomReference objects contain phantom references to their referents. You can use phantom references to perform post-finalization cleanup
on the referents.