Collaborating with the Java Memory Manager
By Brian Gilstrap, Principal Software Engineer, Object Computing, Inc. (OCI)
Java developers are all familiar with the Java garbage collector, which eliminates the need to explicitly deallocate objects. However, few know that there is a package in Java 2[TM] (JDK 1.2 and later) which allows them to coordinate their activities with the garbage collector. This package java.lang.ref, provides a number of classes to allow developers some interaction with garbage collector. To understand how these classes are useful, we must first understand the different types of references.
Types of Object References
All developers use object references when programming. For example, the following line of Java code declares a reference to a String and makes it refer to a string object with contents "hello":
String aHelloString = "hello"
The variable aHelloString is an object reference. In order to distinguish these garden variety object references from the other kinds of references we will be discussing, Java calls this kind of reference a strong reference. This is because the String object referred to by aHelloString is held onto "strongly", meaning it can't be garbage collected as long as it is referred to in this manner.
In addition to normal, strong references, there are three other kinds of references that Java 2[TM] provides. They provide increasingly "weaker" kinds of references, meaning that each successive type of reference places fewer restrictions on the garbage collector. These three types of references are:
-
soft references (java.lang.ref.SoftReference)
-
weak references (java.lang.ref.WeakReference)
-
phantom references (java.lang.ref.PhantomReference)
All of these reference types share certain common characteristics:
-
They all refer to a single object, called the referent (see Figure 1 below)
-
They are created with a constructor that includes a reference to the referent, and will either refer to it or to no object at all.
-
There is a get() method which can be used to get back a strong reference to the referent (if that reference hasn't been cleared)
-
The garbage collector will clear the reference at its discretion when certain conditions are met (the conditions for each kind of reference are discussed below)
These other kinds of references are created by creating an instance of one of the reference classes in the java.lang.ref package (SoftReference, WeakReference, or PhantomReference). There is also a java.lang.ref.Reference class but it is an abstract base class for the other three and cannot be instantiated.
Let�s look at an example,. The following line of code corresponds to Figure 1:
Reference aRef = new SoftReference( new String( �foo� ) );
Figure 1
While we created a SoftReference in the code, we could have created a WeakReference or a PhantomReference and achieved essentially the same state of affairs.
By looking at Figure 1, we can see that these new kinds of reference objects add overhead in terms of space (there is another Java object on the heap, the reference object), and they also cost us some time (the garbage collector interacts with these references in certain ways, which takes time). This leads us to a question: what features do these references offer that would make it worth the time and space to use them? To understand this, we need to look at each kind of reference.
Soft References
Soft references are good for providing caches of objects which can be garbage collected when the Java Virtual Machine (JVM) is running low on memory. In particular, the JVM guarantees a couple of useful things:
-
It will not garbage collect an object as long as there are normal, strong references to it
-
It will not throw an OutOfMemoryError until it has "cleaned up" all softly reachable objects (objects not reachable by strong references but reachable through one or more soft references).
This allows us to use soft references to refer to objects that we could afford to have garbage collected, but which it is convenient to have around until memory becomes tight. Obviously, this means our program has to be able to live without these objects or be able to recreate them. But we can let the garbage collector decide when to collect these objects, continuing the Java goal of letting the programmer avoid explicit memory management.
There is no specific timeframe after an object "goes soft" (becomes reachable through soft references but not through strong references) when the garbage collector will collect the object, but the documentation encourages JVM implementors to prefer collecting older objects and to delay collection as long as is feasible.
Weak References
Weak references are good for providing "canonicalizing mappings". These are mappings from some unique identifier to an object. For example, imagine we have permanent storage (a file, database, etc.) containing a large number of Employee objects, and that we only work with a subset of them at any given time (perhaps there are too many to fit in a running JVM on the machines we have, and we only need to work with a small number of them at any given time). Assume that we uniquely identify employees by an employee number. We could use weak references to refer to all employee objects currently in the JVM and provide a Map (or HashTable) which maps from the employee number to a weak reference to the corresponding Employee object (see Figure 2).
Figure 2
When using this sort of approach, we have a "canonical" mapping between the employee number and the Employee object, meaning there is a one-to-one correspondence between employee numbers as keys in the map, and Employee objects which have that employee number.
We would not want to prevent an Employee object from being garbage collected if no other part of the program is using that object, since we could always retrieve the object from permanent storage. In addition, we want to avoid using a soft reference because this puts pressure on the garbage collector (since it has to wait until running out of memory to reclaim the objects). If we use weak references, we give the garbage collector greater freedom to collect an Employee object more quickly.
This approach allows us to work with a subset of a very large collection of objects (all employees) in a Java program running on a machine with much less memory than we would need if we tried to read in all Employee objects. If we didn�t have weak references, we would have to work around the garbage collector to determine when to "free" a given Employee object, removing all the benefits of the garbage collector and automatic memory management.
Phantom References
Phantom references are a bit different than the previous two kinds of references. It turns out that you can never get back the referent of a PhantomReference once you have created it (calling the get() method always returns null). Yet the PhantomReference still holds onto a reference to its referent. Why is this?
Phantom references are designed to allow for "pre-mortem" cleanup. This means that you can learn about an object which is going to be garbage collected just before it actually gets collected and clean up resources it is using. This is particularly useful if you have several objects collaborating together, and you need to do the cleanup only when all of the objects are no longer using that shared resource. You can keep track of the shared resource and when the last object becomes phantomly reachable do the actual cleanup.
This still leaves one piece of the puzzle missing: how do we know when the object has become phantomly reachable? This leads us to the one remaining class in the java.lang.ref package: ReferenceQueue. When creating a reference object (phantom, weak, or soft), you can specify a reference queue to associate with the reference object. In addition to the behavior already described, the garbage collector will place such reference objects onto the specified ReferenceQueue when it takes action on that reference object.
This allows us to read the references off the queue and know that the garbage collector has done its work with that reference. In the case of a PhantomReference, we can take note of the references which have become phantom and when the reference to the last object using the shared resource "goes phantom", we can clean up that shared resource. We can choose to dedicate a thread to reading entries off a ReferenceQueue, or we can poll it at strategic points in the code.
Summary
The new kinds of references provided by the java.lang.ref package enable developers to build smarter, more efficient programs. And while we can�t create fundamentally new kinds of references, we can subclass the concrete reference classes (SoftReference, WeakReference, and PhantomReference) to add features (this is common with PhantomReference as a way to keep track of the shared resource to be cleaned up).
These classes are not used frequently, but when they are needed they are invaluable. By making them a standard part of Java 2[TM], programmers get a consistent means to build more robust software without resorting to platform/JVM-specific approaches.
由上文可以简单总结如下:
强引用:即是大家都最熟悉的引用,不必多说
软引用:当内存紧张时JVM才会回收软引用指向的资源;
弱引用:当JVM要回收该资源时,JVM将不再犹豫(即不管内存是否紧张);
虚引用:引用已经处于回收并重置内存空间的边缘,程序猿可以利用该引用对马上“死亡”的对象做最后操作;
各引用存在的作用:
软引用上面并没有提到其用处; 弱引用可以用来做“canonicalizing mappings”,即适合用做缓存; 虚引用适合于用来判断共享内存是否适合回收——即程序猿可以得到即将回收对象的信息,并判断他是否适合回收;
说到底,之所以会有那么多中引用的出现,一是为了JVM内部对回收对象的管理和判断,二是为了方便程序猿和JVM之间的相互通信,便于程序猿更好的监控和管理项目,使运行性能更佳。
至于更深一步的理解,以后再在这里续上。