Class WeakHashtable

  • All Implemented Interfaces:
    java.io.Serializable, java.lang.Cloneable, java.util.Map

    public final class WeakHashtable
    extends java.util.Hashtable
    Implementation of Hashtable that uses WeakReference's to hold its keys thus allowing them to be reclaimed by the garbage collector. The associated values are retained using strong references.

    This class follows the semantics of Hashtable as closely as possible. It therefore does not accept null values or keys.

    Note: This is not intended to be a general purpose hash table replacement. This implementation is also tuned towards a particular purpose: for use as a replacement for Hashtable in LogFactory. This application requires good liveliness for get and put. Various tradeoffs have been made with this in mind.

    Usage: typical use case is as a drop-in replacement for the Hashtable used in LogFactory for J2EE environments running 1.3+ JVMs. Use of this class in most cases (see below) will allow classloaders to be collected by the garbage collector without the need to call LogFactory.release(ClassLoader).

    org.apache.commons.logging.LogFactory checks whether this class can be supported by the current JVM, and if so then uses it to store references to the LogFactory implementation it loads (rather than using a standard Hashtable instance). Having this class used instead of Hashtable solves certain issues related to dynamic reloading of applications in J2EE-style environments. However this class requires java 1.3 or later (due to its use of java.lang.ref.WeakReference and associates). And by the way, this extends Hashtable rather than HashMap for backwards compatibility reasons. See the documentation for method LogFactory.createFactoryStore for more details.

    The reason all this is necessary is due to a issue which arises during hot deploy in a J2EE-like containers. Each component running in the container owns one or more classloaders; when the component loads a LogFactory instance via the component classloader a reference to it gets stored in the static LogFactory.factories member, keyed by the component's classloader so different components don't stomp on each other. When the component is later unloaded, the container sets the component's classloader to null with the intent that all the component's classes get garbage-collected. However there's still a reference to the component's classloader from a key in the "global" LogFactory's factories member! If LogFactory.release() is called whenever component is unloaded, the classloaders will be correctly garbage collected; this should be done by any container that bundles commons-logging by default. However, holding the classloader references weakly ensures that the classloader will be garbage collected without the container performing this step.

    Limitations: There is still one (unusual) scenario in which a component will not be correctly unloaded without an explicit release. Though weak references are used for its keys, it is necessary to use strong references for its values.

    If the abstract class LogFactory is loaded by the container classloader but a subclass of LogFactory [LogFactory1] is loaded by the component's classloader and an instance stored in the static map associated with the base LogFactory class, then there is a strong reference from the LogFactory class to the LogFactory1 instance (as normal) and a strong reference from the LogFactory1 instance to the component classloader via getClass().getClassLoader(). This chain of references will prevent collection of the child classloader.

    Such a situation occurs when the commons-logging.jar is loaded by a parent classloader (e.g. a server level classloader in a servlet container) and a custom LogFactory implementation is loaded by a child classloader (e.g. a web app classloader).

    To avoid this scenario, ensure that any custom LogFactory subclass is loaded by the same classloader as the base LogFactory. Creating custom LogFactory subclasses is, however, rare. The standard LogFactoryImpl class should be sufficient for most or all users.

    Since:
    1.1
    Version:
    $Id: WeakHashtable.java 1435077 2013-01-18 10:51:35Z tn $
    See Also:
    Serialized Form
    • Nested Class Summary

      Nested Classes 
      Modifier and Type Class Description
      private static class  WeakHashtable.Entry
      Entry implementation
      private static class  WeakHashtable.Referenced
      Wrapper giving correct symantics for equals and hashcode
      private static class  WeakHashtable.WeakKey
      WeakReference subclass that holds a hard reference to an associated value and also makes accessible the Referenced object holding it.
    • Field Summary

      Fields 
      Modifier and Type Field Description
      private int changeCount  
      private static int MAX_CHANGES_BEFORE_PURGE
      The maximum number of times put() or remove() can be called before the map will be purged of all cleared entries.
      private static int PARTIAL_PURGE_COUNT
      The maximum number of times put() or remove() can be called before the map will be purged of one cleared entry.
      private java.lang.ref.ReferenceQueue queue  
      private static long serialVersionUID
      Serializable version identifier.
    • Constructor Summary

      Constructors 
      Constructor Description
      WeakHashtable()
      Constructs a WeakHashtable with the Hashtable default capacity and load factor.
    • Method Summary

      All Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      boolean containsKey​(java.lang.Object key)  
      java.util.Enumeration elements()  
      java.util.Set entrySet()  
      java.lang.Object get​(java.lang.Object key)  
      boolean isEmpty()  
      java.util.Enumeration keys()  
      java.util.Set keySet()  
      private void purge()
      Purges all entries whose wrapped keys have been garbage collected.
      private void purgeOne()
      Purges one entry whose wrapped key has been garbage collected.
      java.lang.Object put​(java.lang.Object key, java.lang.Object value)  
      void putAll​(java.util.Map t)  
      protected void rehash()  
      java.lang.Object remove​(java.lang.Object key)  
      int size()  
      java.lang.String toString()  
      java.util.Collection values()  
      • Methods inherited from class java.util.Hashtable

        clear, clone, compute, computeIfAbsent, computeIfPresent, contains, containsValue, equals, forEach, getOrDefault, hashCode, merge, putIfAbsent, remove, replace, replace, replaceAll
      • Methods inherited from class java.lang.Object

        finalize, getClass, notify, notifyAll, wait, wait, wait
    • Field Detail

      • serialVersionUID

        private static final long serialVersionUID
        Serializable version identifier.
        See Also:
        Constant Field Values
      • MAX_CHANGES_BEFORE_PURGE

        private static final int MAX_CHANGES_BEFORE_PURGE
        The maximum number of times put() or remove() can be called before the map will be purged of all cleared entries.
        See Also:
        Constant Field Values
      • PARTIAL_PURGE_COUNT

        private static final int PARTIAL_PURGE_COUNT
        The maximum number of times put() or remove() can be called before the map will be purged of one cleared entry.
        See Also:
        Constant Field Values
      • queue

        private final java.lang.ref.ReferenceQueue queue
      • changeCount

        private int changeCount
    • Constructor Detail

      • WeakHashtable

        public WeakHashtable()
        Constructs a WeakHashtable with the Hashtable default capacity and load factor.
    • Method Detail

      • containsKey

        public boolean containsKey​(java.lang.Object key)
        Specified by:
        containsKey in interface java.util.Map
        Overrides:
        containsKey in class java.util.Hashtable
        See Also:
        Hashtable
      • elements

        public java.util.Enumeration elements()
        Overrides:
        elements in class java.util.Hashtable
        See Also:
        Hashtable
      • entrySet

        public java.util.Set entrySet()
        Specified by:
        entrySet in interface java.util.Map
        Overrides:
        entrySet in class java.util.Hashtable
        See Also:
        Hashtable
      • get

        public java.lang.Object get​(java.lang.Object key)
        Specified by:
        get in interface java.util.Map
        Overrides:
        get in class java.util.Hashtable
        See Also:
        Hashtable
      • keys

        public java.util.Enumeration keys()
        Overrides:
        keys in class java.util.Hashtable
        See Also:
        Hashtable
      • keySet

        public java.util.Set keySet()
        Specified by:
        keySet in interface java.util.Map
        Overrides:
        keySet in class java.util.Hashtable
        See Also:
        Hashtable
      • put

        public java.lang.Object put​(java.lang.Object key,
                                    java.lang.Object value)
        Specified by:
        put in interface java.util.Map
        Overrides:
        put in class java.util.Hashtable
        See Also:
        Hashtable
      • putAll

        public void putAll​(java.util.Map t)
        Specified by:
        putAll in interface java.util.Map
        Overrides:
        putAll in class java.util.Hashtable
        See Also:
        Hashtable
      • values

        public java.util.Collection values()
        Specified by:
        values in interface java.util.Map
        Overrides:
        values in class java.util.Hashtable
        See Also:
        Hashtable
      • remove

        public java.lang.Object remove​(java.lang.Object key)
        Specified by:
        remove in interface java.util.Map
        Overrides:
        remove in class java.util.Hashtable
        See Also:
        Hashtable
      • isEmpty

        public boolean isEmpty()
        Specified by:
        isEmpty in interface java.util.Map
        Overrides:
        isEmpty in class java.util.Hashtable
        See Also:
        Hashtable
      • size

        public int size()
        Specified by:
        size in interface java.util.Map
        Overrides:
        size in class java.util.Hashtable
        See Also:
        Hashtable
      • toString

        public java.lang.String toString()
        Overrides:
        toString in class java.util.Hashtable
        See Also:
        Hashtable
      • rehash

        protected void rehash()
        Overrides:
        rehash in class java.util.Hashtable
        See Also:
        Hashtable
      • purge

        private void purge()
        Purges all entries whose wrapped keys have been garbage collected.
      • purgeOne

        private void purgeOne()
        Purges one entry whose wrapped key has been garbage collected.