Java/Collections Data Structure/SoftReference

Материал из Java эксперт
Перейти к: навигация, поиск

An implementation of Set that manages a map of soft references to the set values.

   <source lang="java">
  

/*

* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.reflect.Array; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; /**

* An implementation of Set that manages a map of soft references to the set
* values. The map is keyed by the value hashCode and so this is only useful for
* value whose hashCode is a valid identity representation (String, primative
* wrappers, etc).
* 
* @author Scott.Stark@jboss.org
* @version $Revision: 2787 $
*/

@SuppressWarnings("unchecked") public class SoftSet implements Set {

 private HashMap map = new HashMap();
 /** The queue of garbage collected soft references */
 private ReferenceQueue gcqueue = new ReferenceQueue();
 static class ComparableSoftReference extends SoftReference {
   private Integer key;
   ComparableSoftReference(Integer key, Object o, ReferenceQueue q) {
     super(o, q);
     this.key = key;
   }
   Integer getKey() {
     return key;
   }
 }
 static class ComparableSoftReferenceIterator implements Iterator {
   Iterator theIter;
   ComparableSoftReferenceIterator(Iterator theIter) {
     this.theIter = theIter;
   }
   public boolean hasNext() {
     return theIter.hasNext();
   }
   public Object next() {
     ComparableSoftReference csr = (ComparableSoftReference) theIter.next();
     return csr.get();
   }
   public void remove() {
     theIter.remove();
   }
 }
 /**
  * 
  */
 public SoftSet() {
 }
 public int size() {
   processQueue();
   return map.size();
 }
 public boolean isEmpty() {
   processQueue();
   return map.isEmpty();
 }
 public boolean contains(Object o) {
   processQueue();
   Integer key = new Integer(o.hashCode());
   boolean contains = map.containsKey(key);
   return contains;
 }
 public Iterator iterator() {
   processQueue();
   Iterator theIter = map.values().iterator();
   return new ComparableSoftReferenceIterator(theIter);
 }
 public Object[] toArray() {
   processQueue();
   return toArray(new Object[0]);
 }
 public Object[] toArray(Object[] a) {
   processQueue();
   int size = map.size();
   Object[] array = {};
   if (a.length >= size)
     array = a;
   Iterator iter = map.values().iterator();
   int index = 0;
   while (iter.hasNext()) {
     ComparableSoftReference csr = (ComparableSoftReference) iter.next();
     Object value = csr.get();
     // Create the correct array type
     if (array.length == 0) {
       if (value == null) {
         index++;
         continue;
       }
       Array.newInstance(value.getClass(), size);
     }
     array[index] = value;
     index++;
   }
   return array;
 }
 public boolean add(Object o) {
   processQueue();
   Integer key = new Integer(o.hashCode());
   ComparableSoftReference sr = new ComparableSoftReference(key, o, gcqueue);
   return map.put(key, sr) == null;
 }
 public boolean remove(Object o) {
   processQueue();
   Integer key = new Integer(o.hashCode());
   return map.remove(key) != null;
 }
 public boolean containsAll(Collection c) {
   processQueue();
   Iterator iter = c.iterator();
   boolean contains = true;
   while (iter.hasNext()) {
     Object value = iter.next();
     Integer key = new Integer(value.hashCode());
     contains &= map.containsKey(key);
   }
   return contains;
 }
 public boolean addAll(Collection c) {
   processQueue();
   Iterator iter = c.iterator();
   boolean added = false;
   while (iter.hasNext()) {
     Object value = iter.next();
     Integer key = new Integer(value.hashCode());
     ComparableSoftReference sr = new ComparableSoftReference(key, value, gcqueue);
     added |= map.put(key, sr) == null;
   }
   return added;
 }
 public boolean retainAll(Collection c) {
   Iterator iter = iterator();
   boolean removed = false;
   while (iter.hasNext()) {
     Object value = iter.next();
     if (c.contains(value) == false) {
       iter.remove();
       removed = true;
     }
   }
   return removed;
 }
 public boolean removeAll(Collection c) {
   processQueue();
   Iterator iter = c.iterator();
   boolean removed = false;
   while (iter.hasNext()) {
     Object value = iter.next();
     removed |= remove(value);
   }
   return removed;
 }
 public void clear() {
   while (gcqueue.poll() != null)
     ;
   map.clear();
 }
 public boolean equals(Object o) {
   return map.equals(o);
 }
 public int hashCode() {
   return map.hashCode();
 }
 /**
  * Iterate through the gcqueue for for any cleared reference, remove the
  * associated value from the underlying set.
  */
 private void processQueue() {
   ComparableSoftReference cr;
   while ((cr = (ComparableSoftReference) gcqueue.poll()) != null) {
     map.remove(cr.getKey());
   }
 }

}


 </source>
   
  
 
  



A phantom reference is used to determine when an object is just about to be reclaimed.

   <source lang="java">
 

import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; public class Main {

 public static void main(String[] argv) throws Exception {
   ReferenceQueue rq = new ReferenceQueue();
   PhantomReference<String> pr = new PhantomReference<String>("object", rq);
   while (true) {
     Reference r = rq.remove();
     if (r == pr) {
       // about to be reclaimed.
       r.clear();
     }
   }
 }

}


 </source>
   
  
 
  



A soft reference holds onto its referent until memory becomes low.

   <source lang="java">
 

import java.lang.ref.SoftReference; public class Main {

 public static void main(String[] argv) throws Exception {
   SoftReference<String> sr = new SoftReference<String>("object");
   Object o = sr.get();
   if (o != null) {
     System.out.println(o);
   } else {
     System.out.println("collected or has been reclaimed");
   }
 }

}


 </source>
   
  
 
  



A weak reference is used to determine when an object is no longer being referenced.

   <source lang="java">
 

import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; public class Main {

 public static void main(String[] argv) throws Exception {
   ReferenceQueue rq = new ReferenceQueue();
   WeakReference<String> wr = new WeakReference<String>("string", rq);
   while (true) {
     Reference r = rq.remove();
     if (r == wr) {
       System.out.println("no longer referenced");
     }
   }
 }

}


 </source>
   
  
 
  



Cache based on SoftReference

   <source lang="java">
  

/* Copyright 2004 The Apache Software Foundation

*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*  limitations under the License.
*/

// Revised from xmlbeans import java.util.HashMap; import java.lang.ref.SoftReference; /**

* @author Cezar Andrei (cezar.andrei at bea.ru)
*         Date: Apr 26, 2005
*/

public class SoftCache {

   private HashMap map = new HashMap();
   public Object get(Object key)
   {
       SoftReference softRef = (SoftReference)map.get(key);
       if (softRef==null)
           return null;
       return softRef.get();
   }
   public Object put(Object key, Object value)
   {
       SoftReference softRef = (SoftReference)map.put(key, new SoftReference(value));
       if (softRef==null)
           return null;
       Object oldValue = softRef.get();
       softRef.clear();
       return oldValue;
   }
   public Object remove(Object key)
   {
       SoftReference softRef = (SoftReference)map.remove(key);
       if (softRef==null)
           return null;
       Object oldValue = softRef.get();
       softRef.clear();
       return oldValue;
   }

}


 </source>
   
  
 
  



Soft ValueMap

   <source lang="java">
 

/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.

* 
* This program and the accompanying materials are made available under
* the terms of the Common Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/cpl-v10.html
* 
* $Id: SoftValueMap.java,v 1.1.1.1 2004/05/09 16:57:55 vlad_r Exp $
*/

import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.Collection; import java.util.Map; import java.util.Set; // ---------------------------------------------------------------------------- /**

* MT-safety: an instance of this class is not safe for access from
* multiple concurrent threads [even if access is done by a single thread at a
* time]. The caller is expected to synchronize externally on an instance [the
* implementation does not do internal synchronization for the sake of efficiency].
* java.util.ConcurrentModificationException is not supported either.
*
* @author (C) 2002, Vlad Roubtsov
*/

public final class SoftValueMap implements Map {

   // public: ................................................................
   // TODO: for caching, does clearing of entries make sense? only to save
   // entry memory -- which does not make sense if the set of key values is not
   // growing over time... on the other hand, if the key set is unbounded,
   // entry clearing is needed so that the hash table does not get polluted with
   // empty-valued entries 
   // TODO: provide mode that disables entry clearing 
   // TODO: add shrinking rehashes (is it worth it?)
   /**
    * Equivalent to SoftValueMap(1, 1).
    */
   public SoftValueMap ()
   {
       this (1, 1);
   }
   
   /**
    * Equivalent to SoftValueMap(11, 0.75F, getClearCheckFrequency, putClearCheckFrequency).
    */
   public SoftValueMap (final int readClearCheckFrequency, final int writeClearCheckFrequency)
   {
       this (11, 0.75F, readClearCheckFrequency, writeClearCheckFrequency);
   }
   
   /**
    * Constructs a SoftValueMap with specified initial capacity, load factor,
    * and cleared value removal frequencies.
    *
    * @param initialCapacity initial number of hash buckets in the table
    * [may not be negative, 0 is equivalent to 1].
    * @param loadFactor the load factor to use to determine rehashing points
    * [must be in (0.0, 1.0] range].
    * @param readClearCheckFrequency specifies that every readClearCheckFrequency
    * {@link #get} should check for and remove all mappings whose soft values
    * have been cleared by the garbage collector [may not be less than 1].
    * @param writeClearCheckFrequency specifies that every writeClearCheckFrequency
    * {@link #put} should check for and remove all mappings whose soft values
    * have been cleared by the garbage collector [may not be less than 1].
    */
   public SoftValueMap (int initialCapacity, final float loadFactor, final int readClearCheckFrequency, final int writeClearCheckFrequency)
   {
       if (initialCapacity < 0)
           throw new IllegalArgumentException ("negative input: initialCapacity [" + initialCapacity + "]");
       if ((loadFactor <= 0.0) || (loadFactor >= 1.0 + 1.0E-6))
           throw new IllegalArgumentException ("loadFactor not in (0.0, 1.0] range: " + loadFactor);
       if (readClearCheckFrequency < 1)
           throw new IllegalArgumentException ("readClearCheckFrequency not in [1, +inf) range: " + readClearCheckFrequency);
       if (writeClearCheckFrequency < 1)
           throw new IllegalArgumentException ("writeClearCheckFrequency not in [1, +inf) range: " + writeClearCheckFrequency);
       
       if (initialCapacity == 0) initialCapacity = 1;
       
       m_valueReferenceQueue = new ReferenceQueue ();
       
       m_loadFactor = loadFactor;
       m_sizeThreshold = (int) (initialCapacity * loadFactor);
       
       m_readClearCheckFrequency = readClearCheckFrequency;
       m_writeClearCheckFrequency = writeClearCheckFrequency;
       
       m_buckets = new SoftEntry [initialCapacity];
   }
   
   
   // unsupported operations:
       
   public boolean equals (final Object rhs)
   {
       throw new UnsupportedOperationException ("not implemented: equals");
   }
   
   public int hashCode ()
   {
       throw new UnsupportedOperationException ("not implemented: hashCode");
   }
   
   
   /**
    * Overrides Object.toString() for debug purposes.
    */
   public String toString ()
   {
       final StringBuffer s = new StringBuffer ();
       debugDump (s);
       
       return s.toString ();
   }
   
   
   /**
    * Returns the number of key-value mappings in this map. Some of the values
* may have been cleared already but not removed from the table.

* * NOTE: in contrast with the java.util.WeakHashMap implementation, * this is a constant time operation. */ public int size () { return m_size; } /** * Returns "false" is this map contains key-value mappings (even if some of * the values may have been cleared already but not removed from the table).<P> * * NOTE: in contrast with the java.util.WeakHashMap implementation, * this is a constant time operation. */ public boolean isEmpty () { return m_size == 0; } /** * Returns the value that is mapped to a given "key". Returns * null if (a) this key has never been mapped or (b) a previously mapped * value has been cleared by the garbage collector and removed from the table. * * @param key mapping key [may not be null]. * * @return Object value mapping for "key" [can be null]. */ public Object get (final Object key) { if (key == null) throw new IllegalArgumentException ("null input: key"); if ((++ m_readAccessCount % m_readClearCheckFrequency) == 0) removeClearedValues (); // index into the corresponding hash bucket: final int keyHashCode = key.hashCode (); final SoftEntry [] buckets = m_buckets; final int bucketIndex = (keyHashCode & 0x7FFFFFFF) % buckets.length; Object result = null; // traverse the singly-linked list of entries in the bucket: for (SoftEntry entry = buckets [bucketIndex]; entry != null; entry = entry.m_next) { final Object entryKey = entry.m_key; if (IDENTITY_OPTIMIZATION) { // note: this uses an early identity comparison opimization, making this a bit // faster for table keys that do not override equals() [Thread, etc] if ((key == entryKey) || ((keyHashCode == entryKey.hashCode ()) && key.equals (entryKey))) { final Reference ref = entry.m_softValue; result = ref.get (); // may return null to the caller // [see comment for ENQUEUE_FOUND_CLEARED_ENTRIES] if (ENQUEUE_FOUND_CLEARED_ENTRIES && (result == null)) { ref.enqueue (); } return result; } } else { if ((keyHashCode == entryKey.hashCode ()) && key.equals (entryKey)) { final Reference ref = entry.m_softValue; result = ref.get (); // may return null to the caller // [see comment for ENQUEUE_FOUND_CLEARED_ENTRIES] if (ENQUEUE_FOUND_CLEARED_ENTRIES && (result == null)) { ref.enqueue (); } return result; } } } return null; } /** * Updates the table to map "key" to "value". Any existing mapping is overwritten. * * @param key mapping key [may not be null]. * @param value mapping value [may not be null]. * * @return Object previous value mapping for "key" [null if no previous mapping * existed or its value has been cleared by the garbage collector and removed from the table]. */ public Object put (final Object key, final Object value) { if (key == null) throw new IllegalArgumentException ("null input: key"); if (value == null) throw new IllegalArgumentException ("null input: value"); if ((++ m_writeAccessCount % m_writeClearCheckFrequency) == 0) removeClearedValues (); SoftEntry currentKeyEntry = null; // detect if "key" is already in the table [in which case, set "currentKeyEntry" to point to its entry]: // index into the corresponding hash bucket: final int keyHashCode = key.hashCode (); SoftEntry [] buckets = m_buckets; int bucketIndex = (keyHashCode & 0x7FFFFFFF) % buckets.length; // traverse the singly-linked list of entries in the bucket: for (SoftEntry entry = buckets [bucketIndex]; entry != null; entry = entry.m_next) { final Object entryKey = entry.m_key; if (IDENTITY_OPTIMIZATION) { // note: this uses an early identity comparison opimization, making this a bit // faster for table keys that do not override equals() [Thread, etc] if ((key == entryKey) || ((keyHashCode == entryKey.hashCode ()) && key.equals (entryKey))) { currentKeyEntry = entry; break; } } else { if ((keyHashCode == entryKey.hashCode ()) && key.equals (entryKey)) { currentKeyEntry = entry; break; } } } if (currentKeyEntry != null) { // replace the current value: final IndexedSoftReference ref = currentKeyEntry.m_softValue; final Object currentKeyValue = ref.get (); // can be null already [no need to work around the get() bug, though] if (currentKeyValue == null) ref.m_bucketIndex = -1; // disable removal by removeClearedValues() [need to do this because of the identity comparison there] currentKeyEntry.m_softValue = new IndexedSoftReference (value, m_valueReferenceQueue, bucketIndex); return currentKeyValue; // may return null to the caller } else { // add a new entry: if (m_size >= m_sizeThreshold) rehash (); // recompute the hash bucket index: buckets = m_buckets; bucketIndex = (keyHashCode & 0x7FFFFFFF) % buckets.length; final SoftEntry bucketListHead = buckets [bucketIndex]; final SoftEntry newEntry = new SoftEntry (m_valueReferenceQueue, key, value, bucketListHead, bucketIndex); buckets [bucketIndex] = newEntry; ++ m_size; return null; } } public Object remove (final Object key) { if (key == null) throw new IllegalArgumentException ("null input: key"); if ((++ m_writeAccessCount % m_writeClearCheckFrequency) == 0) removeClearedValues (); // index into the corresponding hash bucket: final int keyHashCode = key.hashCode (); final SoftEntry [] buckets = m_buckets; final int bucketIndex = (keyHashCode & 0x7FFFFFFF) % buckets.length; Object result = null; // traverse the singly-linked list of entries in the bucket: for (SoftEntry entry = buckets [bucketIndex], prev = null; entry != null; prev = entry, entry = entry.m_next) { final Object entryKey = entry.m_key; if ((IDENTITY_OPTIMIZATION && (entryKey == key)) || ((keyHashCode == entryKey.hashCode ()) && key.equals (entryKey))) { if (prev == null) // head of the list { buckets [bucketIndex] = entry.m_next; } else { prev.m_next = entry.m_next; } final IndexedSoftReference ref = entry.m_softValue; result = ref.get (); // can be null already [however, no need to work around 4485942] // [regardless of whether the value has been enqueued or not, disable its processing by removeClearedValues() since the entire entry is removed here] ref.m_bucketIndex = -1; // help GC: entry.m_softValue = null; entry.m_key = null; entry.m_next = null; entry = null; -- m_size; break; } } return result; } public void clear () { final SoftEntry [] buckets = m_buckets; for (int b = 0, bLimit = buckets.length; b < bLimit; ++ b) { for (SoftEntry entry = buckets [b]; entry != null; ) { final SoftEntry next = entry.m_next; // remember next pointer because we are going to reuse this entry // [regardless of whether the value has been enqueued or not, disable its processing by removeClearedValues()] entry.m_softValue.m_bucketIndex = -1; // help GC: entry.m_softValue = null; entry.m_next = null; entry.m_key = null; entry = next; } buckets [b] = null; } m_size = 0; m_readAccessCount = 0; m_writeAccessCount = 0; } // unsupported operations: public boolean containsKey (final Object key) { throw new UnsupportedOperationException ("not implemented: containsKey"); } public boolean containsValue (final Object value) { throw new UnsupportedOperationException ("not implemented: containsValue"); } public void putAll (final Map map) { throw new UnsupportedOperationException ("not implemented: putAll"); } public Set keySet () { throw new UnsupportedOperationException ("not implemented: keySet"); } public Set entrySet () { throw new UnsupportedOperationException ("not implemented: entrySet"); } public Collection values () { throw new UnsupportedOperationException ("not implemented: values"); } // protected: ............................................................. // package: ............................................................... void debugDump (final StringBuffer out) { if (out != null) { out.append (getClass ().getName ().concat ("@").concat (Integer.toHexString (System.identityHashCode (this)))); out.append (EOL); out.append ("size = " + m_size + ", bucket table size = " + m_buckets.length + ", load factor = " + m_loadFactor + EOL); out.append ("size threshold = " + m_sizeThreshold + ", get clear frequency = " + m_readClearCheckFrequency + ", put clear frequency = " + m_writeClearCheckFrequency + EOL); out.append ("get count: " + m_readAccessCount + ", put count: " + m_writeAccessCount + EOL); } } // private: ............................................................... /** * An extension of WeakReference that can store an index of the bucket it * is associated with. */ static class IndexedSoftReference extends SoftReference { IndexedSoftReference (final Object referent, ReferenceQueue queue, final int bucketIndex) { super (referent, queue); m_bucketIndex = bucketIndex; } int m_bucketIndex; } // end of nested class /** * The structure used for chaining colliding keys. */ static class SoftEntry { SoftEntry (final ReferenceQueue valueReferenceQueue, final Object key, Object value, final SoftEntry next, final int bucketIndex) { m_key = key; m_softValue = new IndexedSoftReference (value, valueReferenceQueue, bucketIndex); // ... do not retain a strong reference to the value value = null; m_next = next; } IndexedSoftReference m_softValue; // soft reference to the value [never null] Object m_key; // strong reference to the key [never null] SoftEntry m_next; // singly-linked list link } // end of nested class /** * Re-hashes the table into a new array of buckets. During the process * cleared value entries are discarded, making for another efficient cleared * value removal method. */ private void rehash () { // TODO: it is possible to run this method twice, first time using the 2*k+1 prime sequencer for newBucketCount // and then with that value reduced to actually shrink capacity. As it is right now, the bucket table can // only grow in size final SoftEntry [] buckets = m_buckets; final int newBucketCount = (m_buckets.length << 1) + 1; final SoftEntry [] newBuckets = new SoftEntry [newBucketCount]; int newSize = 0; // rehash all entry chains in every bucket: for (int b = 0, bLimit = buckets.length; b < bLimit; ++ b) { for (SoftEntry entry = buckets [b]; entry != null; ) { final SoftEntry next = entry.m_next; // remember next pointer because we are going to reuse this entry IndexedSoftReference ref = entry.m_softValue; // get the soft value reference Object entryValue = ref.get (); // convert the soft reference to a local strong one // skip entries whose keys have been cleared: this also saves on future removeClearedValues() work if (entryValue != null) { // [assertion: "softValue" couldn"t have been enqueued already and can"t be enqueued until strong reference in "entryKey" is nulled out] // index into the corresponding new hash bucket: final int entryKeyHashCode = entry.m_key.hashCode (); final int newBucketIndex = (entryKeyHashCode & 0x7FFFFFFF) % newBucketCount; final SoftEntry bucketListHead = newBuckets [newBucketIndex]; entry.m_next = bucketListHead; newBuckets [newBucketIndex] = entry; // adjust bucket index: ref.m_bucketIndex = newBucketIndex; ++ newSize; entryValue = null; } else { // ["softValue" may or may not have been enqueued already] // adjust bucket index: // [regardless of whether "softValue" has been enqueued or not, disable its removal by removeClearedValues() since the buckets get restructured] ref.m_bucketIndex = -1; } entry = next; } } if (DEBUG) { if (m_size > newSize) System.out.println ("DEBUG: rehash() cleared " + (m_size - newSize) + " values, new size = " + newSize); } m_size = newSize; m_sizeThreshold = (int) (newBucketCount * m_loadFactor); m_buckets = newBuckets; } /** * Removes all entries whose soft values have been cleared _and_ enqueued. * See comments below for why this is safe wrt to rehash(). */ private void removeClearedValues () { int count = 0; next: for (Reference _ref; (_ref = m_valueReferenceQueue.poll ()) != null; ) { // remove entry containing "_ref" using its bucket index and identity comparison: // index into the corresponding hash bucket: final int bucketIndex = ((IndexedSoftReference) _ref).m_bucketIndex; if (bucketIndex >= 0) // skip keys that were already removed by rehash() { // [assertion: this reference was not cleared when the last rehash() ran and so its m_bucketIndex is correct] // traverse the singly-linked list of entries in the bucket: for (SoftEntry entry = m_buckets [bucketIndex], prev = null; entry != null; prev = entry, entry = entry.m_next) { if (entry.m_softValue == _ref) { if (prev == null) // head of the list { m_buckets [bucketIndex] = entry.m_next; } else { prev.m_next = entry.m_next; } entry.m_softValue = null; entry.m_key = null; entry.m_next = null; entry = null; -- m_size; if (DEBUG) ++ count; continue next; } } // no match found this can happen if a soft value got replaced by a put final StringBuffer msg = new StringBuffer ("removeClearedValues(): soft reference [" + _ref + "] did not match within bucket #" + bucketIndex + EOL); debugDump (msg); throw new Error (msg.toString ()); } // else: it has already been removed by rehash() or other methods } if (DEBUG) { if (count > 0) System.out.println ("DEBUG: removeClearedValues() cleared " + count + " keys, new size = " + m_size); } } private final ReferenceQueue m_valueReferenceQueue; // reference queue for all references used by SoftEntry objects used by this table private final float m_loadFactor; // determines the setting of m_sizeThreshold private final int m_readClearCheckFrequency, m_writeClearCheckFrequency; // parameters determining frequency of running removeClearedKeys() by get() and put()/remove(), respectively private SoftEntry [] m_buckets; // table of buckets private int m_size; // number of values in the table, not cleared as of last check private int m_sizeThreshold; // size threshold for rehashing private int m_readAccessCount, m_writeAccessCount; private static final String EOL = System.getProperty ("line.separator", "\n"); private static final boolean IDENTITY_OPTIMIZATION = true; // setting this to "true" is an optimization and a workaround for bug 4485942: private static final boolean ENQUEUE_FOUND_CLEARED_ENTRIES = true; private static final boolean DEBUG = false; } // end of class // ---------------------------------------------------------------------------- </source>

Testing PhantomReference

   <source lang="java">
 

import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.HashMap; public class Main {

 public static void main(String[] args) {
   ReferenceQueue referenceQueue = new ReferenceQueue();
   Object object = new Object() {
     public String toString() {
       return "Referenced Object";
     }
   };
   Object data = new Object() {
     public String toString() {
       return "Data";
     }
   };
   HashMap map = new HashMap();
   Reference reference = null;
   System.out.println("Testing PhantomReference.");
   reference = new PhantomReference(object, referenceQueue);
   map.put(reference, data);
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
   System.gc();
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
   object = null;
   data = null;
   System.gc();
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
 }

}


 </source>
   
  
 
  



Testing SoftReference

   <source lang="java">
 

import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.HashMap; public class Main {

 public static void main(String[] args) {
   ReferenceQueue referenceQueue = new ReferenceQueue();
   Object object = new Object() {
     public String toString() {
       return "Referenced Object";
     }
   };
   Object data = new Object() {
     public String toString() {
       return "Data";
     }
   };
   HashMap map = new HashMap();
   Reference reference = new SoftReference(object, referenceQueue);
   map.put(reference, data);
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
   System.gc();
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
   object = null;
   data = null;
   System.gc();
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
 }

}


 </source>
   
  
 
  



Testing WeakReference

   <source lang="java">
 

import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.HashMap; public class Main {

 public static void main(String[] args) {
   ReferenceQueue referenceQueue = new ReferenceQueue();
   Object object = new Object() {
     public String toString() {
       return "Referenced Object";
     }
   };
   Object data = new Object() {
     public String toString() {
       return "Data";
     }
   };
   HashMap map = new HashMap();
   Reference reference = null;
   System.out.println("Testing WeakReference.");
   reference = new WeakReference(object, referenceQueue);
   map.put(reference, data);
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
   System.gc();
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
   object = null;
   data = null;
   System.gc();
   System.out.println(reference.get());
   System.out.println(map.get(reference));
   System.out.println(reference.isEnqueued());
 }

}


 </source>