iamjerryyeung

Thursday, June 15, 2006

different java reference

http://tutorials.beginners.co.uk/integrate_read/i/216/vs_p/p/p/3

Java: Performance Tuning and Memory Management Part 4 - Memory Utilization
Submitted By: Wrox Books
Published Date: 9th March 2001
Viewed: 19,664 times

Full computer related training courses are available in our membership area. To view over 400 Beginners.co.uk courses, click here. An annual training membership costs only £99 OR $149.

Although Java's performance problems have often been exaggerated, it is true that Java programs sometimes use a large amount of memory.

Garbage Collection and the finalize() Method

Before an object is destroyed by the garbage collector, the object's finalize() method is called to allow it to perform any necessary cleanup tasks. For example, if the object had used native methods to obtain resources (allocate memory, create network connections, etc.), it might be necessary to release those resources explicitly before the object is destroyed.

Just as you cannot be guaranteed that an object will ever be garbage collected, you cannot be certain that its finalize() method will be called. In Java 1.1, you could use the static runFinalizersOnExit() method in the java.lang.System class to force each object's finalize() method to be called prior to the termination of the JVM process. However, that method is deprecated in Java 2 because of problems that can occur when an object's finalize() method is invoked by one thread while another thread may be modifying the same object's state. In other words, runFinalizersOnExit() causes the finalize() method to be called for every object not previously finalized, even those that are still reachable.

To illustrate the fact that it's not possible to predict when or if the garbage collector will run, we can make some minor modifications to the GarbageTest class defined earlier. Specifically, we'll override finalize() in GarbageTest and TestClass so that they'll issue messages before instances are garbage collected:
public class GarbageTest {

protected Object objectRef;

public static void main(String[] args) {
GarbageTest gt = new GarbageTest();
System.out.println("Exiting main()");
}

public GarbageTest() {
objectRef = new TestClass(this);
}

protected void finalize() throws Throwable {
System.out.println("Finalizing GarbageTest");
}

class TestClass {

Object testref;

public TestClass(Object objref) {
testref = objref;
}

protected void finalize() throws Throwable {
System.out.println("Finalizing TestClass");
}

}

}

When this code is executed, neither finalize() method is called prior to termination of the JVM, so the only output produced is the message issued from the main() method as shown below:

Exiting main()

However, the main() method can be modified to force the execution of the objects' finalize() methods prior to exit by using the deprecated runFinalizersOnExit() method as shown below:

public static void main(String[] args) {
GarbageTest gt = new GarbageTest();
System.runFinalizersOnExit(true);
System.out.println("Exiting main()");
}

Executing this modified version of GarbageTest produces the following output, illustrating that the finalize() methods of both the GarbageTest and TestClass instances were called prior to termination of the JVM:

Exiting main()
Finalizing TestClass
Finalizing GarbageTest

Ensuring that the finalize() methods are called without using the deprecated runFinalizersOnExit() method is slightly more complex, and as we'll see, depends upon the garbage collector implementation. For example, we can attempt to make this occur by making the GarbageTest instance unreachable while still executing the main() and then calling System.gc() as shown below:

public static void main(String[] args) {
GarbageTest gt = new GarbageTest();
// System.runFinalizersOnExit(true);
gt = null;
System.gc();
System.out.println("Exiting main()");
}

Since the only reference to the GarbageTest instance is the local variable defined in the main() method, the following line makes that object unreachable:

gt = null;

Since the object is unreachable, the call to System.gc() should cause the garbage collector to reclaim both the GarbageTest object and the TestClass instance to which it maintains a reference, and this is what happens with the "HotSpot" JVM in JDK 1.3 on Win32. However, if you execute this code using release 1.2.2 of JavaSoft's JVM for Win32 platforms, the following output is produced:

Exiting main()

In other words, although both objects are unreachable, they are incorrectly ignored by the garbage collector, which occurs because the garbage collector in 1.2.2 is a partially accurate implementation. Its design makes it unable to identify some objects that are legitimate targets for collection, which can lead to memory leaks in your code. Interestingly enough, if you make the following minor change to the GarbageTest class and execute the modified version, the results will be different:

public static void main(String[] args) {
createAndRelease();
System.gc();
System.out.println("Exiting main()");
}

private static void createAndRelease() {
GarbageTest gt = new GarbageTest();
gt = null;
}

Running this version of the GarbageTest program causes the garbage collector to correctly reclaim both the GarbageTest object and the TestClass instance to which it maintains a reference. The output produced by running the code is shown below:

Finalizing GarbageTest
Finalizing TestClass
Exiting main()

Perhaps even more strangely, eliminating the second line (gt = null;) in the createAndRelease() method shown above causes the garbage collector to again fail to reclaim the two unreachable objects. These variations on the GarbageTest class and the different results that they produce illustrate the unreliability of partially accurate garbage collector algorithms. In a small application that runs for only a short length of time, such omissions may not be significant. However, for an application that is expected to continue running for a long time, the failure to reclaim unused heap space can become a serious problem and even cause the application to fail with an OutOfMemoryError.

Fortunately, the HotSpot technology included in the JDK 1.3 JVM includes a fully accurate garbage collector. It will correctly reclaim the GarbageTest and TestClass objects with any of the implementations of the GarbageTest code described above: with or without the createAndRelease() method, and with or without assigning a null value to the gt variable.

Reference Objects

Prior to Java 2, object instances were garbage collected only when there were no more references to them from live threads. However, Java 2 includes classes that allow you to maintain special types of references to objects, and those new reference types will not prevent objects from being garbage collected.

The classes are defined in the java.lang.ref package, and the Reference class is the superclass of the three new reference types: SoftReference, WeakReference, and PhantomReference. In addition to these classes, the ReferenceQueue class allows you to determine when references to an object have been cleared. References are normally cleared by the garbage collector when it prepares to reclaim an object, but you can explicitly clear a reference by calling the Reference object's clear() method.

The traditional type of object reference provided in earlier releases of Java is referred to as a strong reference, and object instances with strong references cannot be garbage collected. The new reference types provide what are known collectively as weak references, which is somewhat confusing since the WeakReference class represents only one of these types. To create a weak reference to an object, you simply create an instance of the appropriate class, passing its constructor a reference to the object as shown below:

// The myObject variable represents a strong reference
Object o = new Object();
// The softRef variable represents a soft reference
SoftReference softRef = new SoftReference(o);
// The weakRef variable represents a weak reference
WeakReference weakRef = new WeakReference(o);
// The phantomRef variable represents a phantom reference
// When creating a phantom reference, you must specify a reference
// queue (discussed later)
ReferenceQueue rq = new ReferenceQueue();
PhantomReference phantomRef = new PhantomReference(o, rg);

SoftReference and WeakReference instances can be used to create additional (possibly strong) references to the referenced object (also known as the referent) through the get() method. For example, the following code illustrates how a SoftReference is created, and is later used to create a strong reference to the referent:

byte[] buffer = new byte[10000];
SoftReference bufferRef = new SoftReference(buffer);
// Eliminate the strong reference
buffer = null;
// ...

// Create a new strong reference
byte[] bufferData = (byte[])(bufferRef.get());

Note, however, that the get() method will return a null value if the reference was reclaimed by the garbage collector after the weak reference was created. Therefore, when you do use get() to attempt to access the referent, you should ensure that it did not return a null before you use the return value.

An object is classified based on the strongest type of reference that exists to that object, and will be one of the following five values:

A strongly reachable object has strong references and may have soft, weak, or phantom references.
A softly reachable object has soft references and may have weak or phantom references, but does not have strong references.
A weakly reachable object has weak references and may have phantom references, but does not have strong or soft references.
A phantomly reachable object has only phantom references; it does not have strong, soft, or weak references.
An unreachable object is one for which there are no references of any kind.
In contrast, an object that is referenced through a chain of references is only as reachable as the weakest reference in the chain. For example, the following illustrates how a string of references can be created:

SoftReference sr = new SoftReference(new WeakReference(new Date()));

In this case, the Date instance is only weakly reachable, because it is referenced through a chain of two references, one of which is a WeakReference.

At this point, you may be wondering why there are three different types of weak references, and how their behavior differs. SoftReference and WeakReference are very similar in function and behavior, but there is one important difference. Garbage collector implementations are discouraged (but not prevented) from reclaiming softly reachable objects, especially those that have been created or accessed recently. In contrast, the garbage collector will reclaim a weakly reachable object just as if no references existed to it at all.

Phantom references are very different from the other two types, and are intended to be used specifically to allow you to determine when an object is about to be destroyed by the garbage collector. In fact, the get() method of a PhantomReference always returns null, even when the referent has not yet been garbage collected. After the referent's finalize() method has been called but before it is destroyed, any PhantomReference instances that refer to the referent are added to the appropriate ReferenceQueue.

Garbage collection of the referent will not be completed until the PhantomReference's clear() method is called, so it is the responsibility of your application to do so when using phantom references. Your code should wait for PhantomReference instances to be placed on the queue by the garbage collector, perform the appropriate cleanup for the referent, and then call the clear() method to allow the referent to be reclaimed.

The methods defined in ReferenceQueue simply allow you to retrieve Reference objects that have been placed on the queue. One implementation of the remove() method waits indefinitely until a Reference appears in the queue and returns that object. The other version of remove() allows you to specify the maximum number of milliseconds that it will wait for a Reference to be queued, and if none becomes available within that time, it returns a null value.

Finally, the poll() method always returns immediately, providing the caller with either a Reference that was queued or with a null value if none is available. In other words, the remove() method blocks the calling thread until an entry is available in queue or until the specified amount of time elapses, while poll() never blocks the caller.

How Reference Objects can be Used

Now that the behavior of the three Reference subclasses has been described, we'll briefly examine how each one can be used. Although many of the examples shown here use the java.lang.ref classes directly, you'll often find it more useful to subclass one of those when using weak references with your application. By creating a subclass, you can associate more information with the reference about how it is used and/or what action to take when you detect that its referent was cleared.

SoftReference

You can use SoftReference instances to cache information that should be discarded when your application begins to run low on memory. For example, if you're reading information from a database or files from a web browser, it may be useful to cache that data in memory as long as it does not cause your application to run out of memory. When the application needs to read information that was previously retrieved from the database or the network, it can first check the SoftReference-based cache. If the reference has not been cleared, the data can be quickly returned from the memory cache, but if it has been cleared, the data can again be retrieved from the database or network.

WeakReference

You'll sometimes wish to associate information with an object without preventing that object from being garbage collected, and you'll want the information to be destroyed when the object is reclaimed. If you were to use a "traditional" (strong) reference to associate the object with its information the object would never be garbage collected. For example, suppose that you use a Hashtable to maintain a relationship between objects and their information as shown below:

Hashtable ht = new Hashtable();
// ...
MyObject myObj = new MyObject();
ht.put(myObj, "This is the associated information");

The problem with this approach is that as long as a reference to the Hashtable exists and as long as it contains a strong reference to the MyObject instance, that object will never be reclaimed. However, by creating an association with a WeakReference, you can define a mapping that lasts only for the lifetime of the MyObject instance without affecting the garbage collector's ability to reclaim the object. In fact, that is exactly what the java.util.WeakHashMap class does, and an example of how it might be used is provided below:

WeakHashMap map = new WeakHashMap();
// ...
MyObject myObj = new MyObject();
map.put(myObj, "This is the associated information");

Each key in a WeakHashMap is an instance of a subclass of WeakReference, which allows the MyObject instance to be reclaimed by the garbage collector when there are no more strong references to it. Once that does occur, the WeakReference key entry in the map will remain, but its referent will be cleared and it will be placed on a ReferenceQueue. When an entry is added to or removed from the WeakHashMap, it checks the queue to see if any of its entries have been cleared and removes those that represent items that were garbage collected.

PhantomReference

As mentioned earlier, the purpose of the PhantomReference is to allow you to perform cleanup tasks for objects before they are destroyed by the garbage collector. Use of a ReferenceQueue with a SoftReference or WeakReference is optional, but each PhantomReference you construct must be associated with a ReferenceQueue as shown below:

MyObject myObj = new MyObject();
ReferenceQueue rq = new ReferenceQueue();
PhantomReference pr = new PhantomReference(myObj, rq);

You may wish to create a separate thread that waits for entries to be added to a ReferenceQueue, or you might design your code so that it periodically calls the poll() method. When a PhantomReference is retrieved from the queue, the appropriate cleanup should be performed for its reference and the clear() method called to allow the referent to be garbage collected.

Summary

In this chapter, we have looked at:

Using HPROF to examine the performance of a Java application, and to locate the source of a performance problem
Tips for improving the performance of your applications
The various types of Java compilers that are available, including Just-In-Time compilers and Sun's HotSpot technology
Memory management, including examination of garbage collection and reference objects

0 Comments:

Post a Comment

<< Home