Weak references Canonicalizing Objects

- 87 - by you. If the object is not supposed to be changed, you can throw an exception on any update method. Alternatively, if you want some objects to be canonicalized but with copy-on-write behavior, you can allow the updater to return a noncanonicalized copy of the canonical object. [5] Beware that using a subclass may break the superclass semantics. Note that it makes no sense to build a table of millions or even thousands of strings or other objects if the time taken to test for, access, and update objects in the table is longer than the time you are saving canonicalizing them.

4.2.4.3 Weak references

One technique for maintaining collections of objects that can grow too large is the use of WeakReference s from the java.lang.ref package in Java 2. If you need to maintain one or more pools of objects with a large number of objects being held, you may start coming up against memory limits of the VM. In this case, you should consider using WeakReference objects to hold on to your pool elements. Objects referred to by WeakReference s can be automatically garbage- collected if memory gets low enough see Reference Objects . Reference Objects In many ways, you can think of Reference objects as normal objects that have a private Object instance variable. You can access the private object termed the referent using the Reference.get method. However, Reference objects differ from normal objects in one hugely important way. The garbage collector may be allowed to clear Reference objects when it decides space is low enough . Clearing the Reference object sets the referent to null . For example, say you assign an object to a Reference . Later you test to see if the referent is null . It could be null if, between the assignment and the test, the garbage collector kicked in and decided to reclaim space: Reference ref = new WeakReferencesomeObject; ref.get is someObject at the moment Now do something that creates lots of objects, making the garbage collector try to find more memory space doSomething ; now test if ref is null if ref.get == null System.out.printlnThe garbage collector deleted my ref; else System.out.printlnref object is still here; Note that the referent can be garbage-collected at any time, as long as there are no other strong references referring to it. In the example, ref.get can become null only if there are no other non- Reference objects referring to someObject . The advantage of Reference s is that you can use them to hang on to objects that you want to reuse but are not needed immediately. If memory space gets too low, those objects not currently being used are automatically reclaimed by the garbage collector. This means that you subsequently need to create objects instead of reusing them, but that is preferable to having the program crash from lack of memory. To delete the reference object itself when the referent is nulled, you need to create the reference with a ReferenceQueue instance. When the reference object is cleared, it is added to the - 88 - ReferenceQueue instance and can then be processed by the application, e.g., explicitly deleted from a hash table in which it may be a key. There are three Reference types in Java 2. WeakReference s and SoftReference s differ essentially in the order in whcih the garbage collector clears them. Basically, the garbage collector does not clear WeakReference objects until all SoftReference s have been cleared. PhantomReference s not addressed here are not cleared automatically by the garbage collector and are intended for use in a different way. The concept behind this differentiation is that SoftReference s are intended to be used for caches that may need to have memory automatically freed, and WeakReference s are intended for canonical tables that may need to have memory automatically freed. The rationale is that caches normally take up more space and are the first to be reclaimed when memory gets low. Canonical tables are normally smaller, and developers prefer them not to be garbage-collected unless memory gets really low. This differentiation between the two reference types allows cache memory to be freed up first if memory gets low; only when there is no more cache memory to be freed does the garbage collector start looking at canonical table memory. Java 2 comes with a java.util.WeakHashMap class that implements a hash table with keys held by weak references. A WeakReference normally maintains references to elements in a table of canonicalized objects. If memory gets low, any of the objects referred to by the table and not referred to anywhere else in the application except by other weak references are garbage-collected . This does not affect the canonicalization because only those objects not referenced anywhere else are removed. The canonical object can be re-created when required, and this new instance is now the new canonical object: remember that no other references to the object exist, or the original could not have been garbage-collected. For example, a table of canonical Integer objects can be maintained using WeakReference s. This example is not particularly useful: unlike the earlier example, in which Integer objects from 1 to 10 can be referenced directly with no overhead, thus providing a definite speedup for tests, the next example has overheads that would probably swamp any benefits of having canonical Integer s. I present it only as a clear and simple example to illustrate the use of WeakReference s. The example has two iterations: one sets an array of canonical Integer objects up to a value set by the command-line argument; a second loops through to access the first 10 canonical Integer s. If the first loop is large enough or the VM memory is constrained low enough, the garbage collector kicks in and starts reclaiming some of the Integer objects that are all being held by WeakReference s. The second loop then reaccesses the first 10 Integer objects. Earlier, I explicitly held on to five of these Integer objects integers 3 to 7 inclusive in variables so that they could not be garbage-collected, and so that the second loop would reset only the five reclaimed Integer s. When running this test with the VM constrained to 4 MB: java -Xmx4M tuning.reuse.Test 100000 you get the following output: Resetting integer 0 Resetting integer 1 - 89 - Resetting integer 2 Resetting integer 8 Resetting integer 9 The example is defined here. Note the overheads. Even if the reference has not been garbage- collected, you have to access the underlying object and cast it to the desired type: package tuning.reuse; import java.util.; import java.lang.ref.; public class Test { public static void mainString[] args { try { Integer ic = null; int REPEAT = args.length 0 ? Integer.parseIntargs[0] : 10000000; Hang on to the Integer objects from 3 to 7 so that they cannot be garbage collected Integer i3 = getCanonicalInteger3; Integer i4 = getCanonicalInteger4; Integer i5 = getCanonicalInteger5; Integer i6 = getCanonicalInteger6; Integer i7 = getCanonicalInteger7; Loop through getting canonical integers until there is not enough space, and the garbage collector reclaims some. for int i = 0; i REPEAT; i++ ic = getCanonicalIntegeri; Now just re-access the first 10 integers 0 to 9 and the 0, 1, 2, 8, and 9 integers will need to be reset in the access method since they will have been reclaimed for int i = 0; i 10; i++ ic = getCanonicalIntegeri; System.out.printlnic; } catchException e{e.printStackTrace ;} } private static Vector canonicalIntegers = new Vector ; public static Integer getCanonicalIntegerint i { First make sure our collection is big enough if i = canonicalIntegers.size canonicalIntegers.setSizei+1; Now access the canonical value. This element contains null if the the value has never been set or a weak reference that may have been garbage collected WeakReference ref = WeakReference canonicalIntegers.elementAti; Integer canonical_i; if ref == null { never been set, so create and set it now canonical_i = new Integeri; canonicalIntegers.setElementAtnew WeakReferencecanonical_i, i; } - 90 - else if canonical_i = Integer ref.get == null { has been set, but was garbage collected, so recreate and set it now Include a print to see that we are resetting the Integer System.out.printlnResetting integer + i; canonical_i = new Integeri; canonicalIntegers.setElementAtnew WeakReferencecanonical_i, i; } else clause not needed, since the alternative is that the weak ref was present and not garbage collected, so we now have our canonical integer return canonical_i; } }

4.2.4.4 Enumerating constants