Phantom Reference

The PhantomReference class describes a Reference object whose referent is phantom reachable. In addition to inheriting Reference's methods and overriding get(), this generic class provides a single constructor for initializing a PhantomReference object:

■ PhantomReference(T r, ReferenceOueue<? super T> q) encapsulates r's reference. The PhantomReference object behaves as a phantom reference to r. The ReferenceOueue object identified by q is associated with this PhantomReference object. Passing null to q makes no sense because get() is overridden to return null and the PhantomReference object will never be enqueued.

Unlike WeakReference and SoftReference objects, which are enqueued onto their reference queues when their referents become weakly reachable (before finalization), or sometime after their referents become softly reachable (before finalization), PhantomReference objects are enqueued after their referents have been reclaimed.

Although you cannot access a PhantomReference object's referent (its get() method returns null), this class is useful because enqueuing the PhantomReference object tells you exactly when the referent has been removed. Perhaps you want to delay creating a large object until another large object has been removed (to avoid a thrown java.lang.OutOfMemoryError object).

PhantomReference is also useful as a substitute for resurrection (making an unreachable object reachable). Because there is no way to access the referent (get() returns null), which is no longer in memory when the PhantomReference object is enqueued, the object can be cleaned up during the first garbage collection cycle in which that object was discovered to be phantom reachable. You can then clean up related resources after receiving notification via the PhantomReference object's reference queue.

NOTE: Resurrection occurs in the finalize() method when you assign this to a root-set variable. For example, you might specify r = this; within finalize() to assign the unreachable object identified as this to a class field named r.

In contrast, the garbage collector requires at least two garbage collection cycles to determine if an object that overrides finalize() can be garbage collected. When the first cycle detects that the object is eligible for garbage collection, it calls finalize(). Because this method might have resurrected the object, a second garbage collection cycle is needed to determine if resurrection has happened.

CAUTION: Resurrection has been used to implement object pools that recycle the same objects when these objects are expensive (time-wise) to create (database connection objects are an example). Because resurrection exacts a severe performance penalty, and because the PhantomReference class makes resurrection unnecessary, you should avoid using resurrection in your applications.

Listing 6-15 shows how you might use PhantomReference to detect the removal of a large object.

Listing 6-15. Detecting a large object's removal class LargeObject {

private byte[] memory = new byte[l024*1024*50]; // 50 megabytes

public class LargeObjectDemo {

public static void main(String[] args) {

ReferenceQueue<LargeObject> rq;

rq = new ReferenceQueue<LargeObject>();

PhantomReference<LargeObject> pr;

pr = new PhantomReference<LargeObject>(new LargeObject(), rq);

System.out.println("waiting for large object to be removed"); if (counter++ == 10)

System.out.println("large object removed");

Listing 6-15 declares a LargeObject class whose private memory array occupies 50MB. If your Java implementation throws OutOfMemoryError when you run this application, you might need to reduce the size of this array.

The main() method first creates a ReferenceOueue object that describes a queue onto which a subsequently created PhantomReference object that contains a LargeObject reference will be enqueued.

main() next creates the PhantomReference object, passing a reference to a newly created LargeObject object and a reference to the previously created ReferenceOueue object to the constructor.

After initializing a counter variable (which determines how many loop iterations pass before another large object is created), and after introducing a local variable named x that will hold a strong reference to another large object, main() enters a polling loop.

The polling loop begins by calling poll() to detect the removal of the LargeObject object from memory. As long as this method returns null, meaning that the LargeObject object is still in memory, the loop outputs a message and increments counter.

When counter's value reaches 10, x is assigned an int-based array containing one million integer elements. Because the reference stored in x is strong, this array will not be garbage collected (before the application ends).

On my platform, assigning this array's reference to x is sufficient for the garbage collector to destroy the LargeObject object. Its PhantomReference object is enqueued onto the rq-referenced ReferenceOueue; poll() returns the PhantomReference object.

Depending on your implementation of the virtual machine, you might or might not observe the large object removed message. If you do not see this message, you might need to increase the size of array x, making sure that OutOfMemoryError is not thrown.

When I run this application on my platform, I observe the following output—you may have to adjust the application's code to observe similar output:

waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed waiting for large object to be removed large object removed

NOTE: For a more useful example of PhantomReference, and for more in-depth knowledge of garbage collection, check out Keith D Gregory's "Java Reference Objects" blog post (http://www.kdgregory.com/index.php?page=java.refobj).

EXERCISES

The following exercises are designed to test your understanding of Java's basic APIs:

1. What constants does Math declare?

2. Why is Math.abs(Integer.MIN_VALUE) equal to Integer.MIN_VALUE?

3. What does Math's random() method accomplish?

4. Identify the five special values that can arise during floating-point calculations.

5. How do Math and StrictMath differ?

6. What is the purpose of strictfp?

7. What is BigDecimal and why might you use this class?

8. Which RoundingMode constant describes the form of rounding commonly taught at school?

9. What is BigInteger?

10. What is the purpose of Package's isSealed() method?

11. True or false: getPackage() requires at least one classfile to be loaded from the package before it returns a Package object describing that package.

12. Identify the two main uses of the primitive wrapper classes.

13. Why should you avoid coding expressions such as ch >= '0' && ch <= '9' (test ch to see if it contains a digit) or ch >= 'A' && ch <= 'Z' (test ch to see if it contains an uppercase letter)?

14. Identify the four kinds of reachability.

15. What is a referent?

16. Which of the References API's classes is the equivalent of Object's finalize() method?

17. Before the era of graphics screens, developers sometimes used a text-based screen to display graphics shapes. For example, a circle might be displayed as follows:

** ** ** ** * * * * ** ** * * * * * * ** ** * * * * * * ** ** * * * * ** ** ** ** *

NOTE: This shape appears elliptical instead of circular because each asterisk's displayed height is greater than its displayed width. If the height and width matched, the shape would appear circular.

Create a Circle application that generates and displays the previous circle shape. Start by creating a two-dimensional screen array of 22 rows by 22 columns. Initialize each array element to the space character (indicating a clear screen). For each integer angle from 0 to 360, compute the x and y coordinates by multiplying a radius value of 10 by each of the cosine and sine of the angle. Add 11 to the x value and 11 to the y value to center the circle shape within the screen array. Assign an asterisk to the array at the resulting (x, y) coordinates. After the loop completes, output the array to the standard output device.

18. A prime number is a positive integer greater than 1 that is evenly divisible only by 1 and itself. Create a PrimeNumberTest application that determines if its solitary integer argument is prime or not prime, and outputs a suitable message. For example, java PrimeNumberTest 289 should output the message 289 is not prime. A simple way to check for primality is to loop from 2 through the square root of the integer argument, and use the remainder operator in the loop to determine if the argument is divided evenly by the loop index. For example, because 6%2 yields a remainder of 0 (2 divides evenly into 6), integer 6 is not a prime number.

Was this article helpful?

0 0

Post a comment