jdk/src/share/classes/com/sun/beans/util/Cache.java
changeset 21793 561ed508292b
child 22577 64bb7e55d3c7
equal deleted inserted replaced
21792:d2bfa69f5523 21793:561ed508292b
       
     1 /*
       
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package com.sun.beans.util;
       
    26 
       
    27 import java.lang.ref.ReferenceQueue;
       
    28 import java.lang.ref.SoftReference;
       
    29 import java.lang.ref.WeakReference;
       
    30 import java.util.Objects;
       
    31 
       
    32 /**
       
    33  * Hash table based implementation of the cache,
       
    34  * which allows to use weak or soft references for keys and values.
       
    35  * An entry in a {@code Cache} will automatically be removed
       
    36  * when its key or value is no longer in ordinary use.
       
    37  *
       
    38  * @author Sergey Malenkov
       
    39  * @since 1.8
       
    40  */
       
    41 public abstract class Cache<K,V> {
       
    42     private static final int MAXIMUM_CAPACITY = 1 << 30; // maximum capacity MUST be a power of two <= 1<<30
       
    43 
       
    44     private final boolean identity; // defines whether the identity comparison is used
       
    45     private final Kind keyKind; // a reference kind for the cache keys
       
    46     private final Kind valueKind; // a reference kind for the cache values
       
    47 
       
    48     private final ReferenceQueue<Object> queue = new ReferenceQueue<>(); // queue for references to remove
       
    49 
       
    50     private volatile CacheEntry<K,V>[] table = newTable(1 << 3); // table's length MUST be a power of two
       
    51     private int threshold = 6; // the next size value at which to resize
       
    52     private int size; // the number of key-value mappings contained in this map
       
    53 
       
    54     /**
       
    55      * Creates a corresponding value for the specified key.
       
    56      *
       
    57      * @param key a key that can be used to create a value
       
    58      * @return a corresponding value for the specified key
       
    59      */
       
    60     public abstract V create(K key);
       
    61 
       
    62     /**
       
    63      * Constructs an empty {@code Cache}.
       
    64      * The default initial capacity is 8.
       
    65      * The default load factor is 0.75.
       
    66      *
       
    67      * @param keyKind   a reference kind for keys
       
    68      * @param valueKind a reference kind for values
       
    69      *
       
    70      * @throws NullPointerException if {@code keyKind} or {@code valueKind} are {@code null}
       
    71      */
       
    72     public Cache(Kind keyKind, Kind valueKind) {
       
    73         this(keyKind, valueKind, false);
       
    74     }
       
    75 
       
    76     /**
       
    77      * Constructs an empty {@code Cache}
       
    78      * with the specified comparison method.
       
    79      * The default initial capacity is 8.
       
    80      * The default load factor is 0.75.
       
    81      *
       
    82      * @param keyKind   a reference kind for keys
       
    83      * @param valueKind a reference kind for values
       
    84      * @param identity  defines whether reference-equality
       
    85      *                  is used in place of object-equality
       
    86      *
       
    87      * @throws NullPointerException if {@code keyKind} or {@code valueKind} are {@code null}
       
    88      */
       
    89     public Cache(Kind keyKind, Kind valueKind, boolean identity) {
       
    90         Objects.requireNonNull(keyKind, "keyKind");
       
    91         Objects.requireNonNull(valueKind, "valueKind");
       
    92         this.keyKind = keyKind;
       
    93         this.valueKind = valueKind;
       
    94         this.identity = identity;
       
    95     }
       
    96 
       
    97     /**
       
    98      * Returns the value to which the specified key is mapped,
       
    99      * or {@code null} if there is no mapping for the key.
       
   100      *
       
   101      * @param key the key whose cached value is to be returned
       
   102      * @return a value to which the specified key is mapped,
       
   103      *         or {@code null} if there is no mapping for {@code key}
       
   104      *
       
   105      * @throws NullPointerException if {@code key} is {@code null}
       
   106      *                              or corresponding value is {@code null}
       
   107      */
       
   108     public final V get(K key) {
       
   109         Objects.requireNonNull(key, "key");
       
   110         removeStaleEntries();
       
   111         int hash = hash(key);
       
   112         // unsynchronized search improves performance
       
   113         // the null value does not mean that there are no needed entry
       
   114         CacheEntry<K,V>[] table = this.table; // unsynchronized access
       
   115         V current = getEntryValue(key, hash, table[index(hash, table)]);
       
   116         if (current != null) {
       
   117             return current;
       
   118         }
       
   119         synchronized (this.queue) {
       
   120             // synchronized search improves stability
       
   121             // we must create and add new value if there are no needed entry
       
   122             int index = index(hash, this.table);
       
   123             current = getEntryValue(key, hash, this.table[index]);
       
   124             if (current != null) {
       
   125                 return current;
       
   126             }
       
   127             V value = create(key);
       
   128             Objects.requireNonNull(value, "value");
       
   129             this.table[index] = new CacheEntry<>(hash, key, value, this.table[index]);
       
   130             if (++this.size >= this.threshold) {
       
   131                 if (this.table.length == MAXIMUM_CAPACITY) {
       
   132                     this.threshold = Integer.MAX_VALUE;
       
   133                 } else {
       
   134                     removeStaleEntries();
       
   135                     table = newTable(this.table.length << 1);
       
   136                     transfer(this.table, table);
       
   137                     // If ignoring null elements and processing ref queue caused massive
       
   138                     // shrinkage, then restore old table.  This should be rare, but avoids
       
   139                     // unbounded expansion of garbage-filled tables.
       
   140                     if (this.size >= this.threshold / 2) {
       
   141                         this.table = table;
       
   142                         this.threshold <<= 1;
       
   143                     } else {
       
   144                         transfer(table, this.table);
       
   145                     }
       
   146                     removeStaleEntries();
       
   147                 }
       
   148             }
       
   149             return value;
       
   150         }
       
   151     }
       
   152 
       
   153     /**
       
   154      * Removes the cached value that corresponds to the specified key.
       
   155      *
       
   156      * @param key the key whose mapping is to be removed from this cache
       
   157      */
       
   158     public final void remove(K key) {
       
   159         if (key != null) {
       
   160             synchronized (this.queue) {
       
   161                 removeStaleEntries();
       
   162                 int hash = hash(key);
       
   163                 int index = index(hash, this.table);
       
   164                 CacheEntry<K,V> prev = this.table[index];
       
   165                 CacheEntry<K,V> entry = prev;
       
   166                 while (entry != null) {
       
   167                     CacheEntry<K,V> next = entry.next;
       
   168                     if (entry.matches(hash, key)) {
       
   169                         if (entry == prev) {
       
   170                             this.table[index] = next;
       
   171                         } else {
       
   172                             prev.next = next;
       
   173                         }
       
   174                         entry.unlink();
       
   175                         break;
       
   176                     }
       
   177                     prev = entry;
       
   178                     entry = next;
       
   179                 }
       
   180             }
       
   181         }
       
   182     }
       
   183 
       
   184     /**
       
   185      * Removes all of the mappings from this cache.
       
   186      * It will be empty after this call returns.
       
   187      */
       
   188     public final void clear() {
       
   189         synchronized (this.queue) {
       
   190             int index = this.table.length;
       
   191             while (0 < index--) {
       
   192                 CacheEntry<K,V> entry = this.table[index];
       
   193                 while (entry != null) {
       
   194                     CacheEntry<K,V> next = entry.next;
       
   195                     entry.unlink();
       
   196                     entry = next;
       
   197                 }
       
   198                 this.table[index] = null;
       
   199             }
       
   200             while (null != this.queue.poll()) {
       
   201                 // Clear out the reference queue.
       
   202             }
       
   203         }
       
   204     }
       
   205 
       
   206     /**
       
   207      * Retrieves object hash code and applies a supplemental hash function
       
   208      * to the result hash, which defends against poor quality hash functions.
       
   209      * This is critical because {@code Cache} uses power-of-two length hash tables,
       
   210      * that otherwise encounter collisions for hashCodes that do not differ
       
   211      * in lower bits.
       
   212      *
       
   213      * @param key the object which hash code is to be calculated
       
   214      * @return a hash code value for the specified object
       
   215      */
       
   216     private int hash(Object key) {
       
   217         if (this.identity) {
       
   218             int hash = System.identityHashCode(key);
       
   219             return (hash << 1) - (hash << 8);
       
   220         }
       
   221         int hash = key.hashCode();
       
   222         // This function ensures that hashCodes that differ only by
       
   223         // constant multiples at each bit position have a bounded
       
   224         // number of collisions (approximately 8 at default load factor).
       
   225         hash ^= (hash >>> 20) ^ (hash >>> 12);
       
   226         return hash ^ (hash >>> 7) ^ (hash >>> 4);
       
   227     }
       
   228 
       
   229     /**
       
   230      * Returns index of the specified hash code in the given table.
       
   231      * Note that the table size must be a power of two.
       
   232      *
       
   233      * @param hash  the hash code
       
   234      * @param table the table
       
   235      * @return an index of the specified hash code in the given table
       
   236      */
       
   237     private static int index(int hash, Object[] table) {
       
   238         return hash & (table.length - 1);
       
   239     }
       
   240 
       
   241     /**
       
   242      * Creates a new array for the cache entries.
       
   243      *
       
   244      * @param size requested capacity MUST be a power of two
       
   245      * @return a new array for the cache entries
       
   246      */
       
   247     @SuppressWarnings("unchecked")
       
   248     private CacheEntry<K,V>[] newTable(int size) {
       
   249         return (CacheEntry<K,V>[]) new CacheEntry[size];
       
   250     }
       
   251 
       
   252     private V getEntryValue(K key, int hash, CacheEntry<K,V> entry) {
       
   253         while (entry != null) {
       
   254             if (entry.matches(hash, key)) {
       
   255                 return entry.value.getReferent();
       
   256             }
       
   257             entry = entry.next;
       
   258         }
       
   259         return null;
       
   260     }
       
   261 
       
   262     private void removeStaleEntries() {
       
   263         Object reference = this.queue.poll();
       
   264         if (reference != null) {
       
   265             synchronized (this.queue) {
       
   266                 do {
       
   267                     if (reference instanceof Ref) {
       
   268                         Ref ref = (Ref) reference;
       
   269                         @SuppressWarnings("unchecked")
       
   270                         CacheEntry<K,V> owner = (CacheEntry<K,V>) ref.getOwner();
       
   271                         if (owner != null) {
       
   272                             int index = index(owner.hash, this.table);
       
   273                             CacheEntry<K,V> prev = this.table[index];
       
   274                             CacheEntry<K,V> entry = prev;
       
   275                             while (entry != null) {
       
   276                                 CacheEntry<K,V> next = entry.next;
       
   277                                 if (entry == owner) {
       
   278                                     if (entry == prev) {
       
   279                                         this.table[index] = next;
       
   280                                     } else {
       
   281                                         prev.next = next;
       
   282                                     }
       
   283                                     entry.unlink();
       
   284                                     break;
       
   285                                 }
       
   286                                 prev = entry;
       
   287                                 entry = next;
       
   288                             }
       
   289                         }
       
   290                     }
       
   291                     reference = this.queue.poll();
       
   292                 }
       
   293                 while (reference != null);
       
   294             }
       
   295         }
       
   296     }
       
   297 
       
   298     private void transfer(CacheEntry<K,V>[] oldTable, CacheEntry<K,V>[] newTable) {
       
   299         int oldIndex = oldTable.length;
       
   300         while (0 < oldIndex--) {
       
   301             CacheEntry<K,V> entry = oldTable[oldIndex];
       
   302             oldTable[oldIndex] = null;
       
   303             while (entry != null) {
       
   304                 CacheEntry<K,V> next = entry.next;
       
   305                 if (entry.key.isStale() || entry.value.isStale()) {
       
   306                     entry.unlink();
       
   307                 } else {
       
   308                     int newIndex = index(entry.hash, newTable);
       
   309                     entry.next = newTable[newIndex];
       
   310                     newTable[newIndex] = entry;
       
   311                 }
       
   312                 entry = next;
       
   313             }
       
   314         }
       
   315     }
       
   316 
       
   317     /**
       
   318      * Represents a cache entry (key-value pair).
       
   319      */
       
   320     private final class CacheEntry<K,V> {
       
   321         private final int hash;
       
   322         private final Ref<K> key;
       
   323         private final Ref<V> value;
       
   324         private volatile CacheEntry<K,V> next;
       
   325 
       
   326         /**
       
   327          * Constructs an entry for the cache.
       
   328          *
       
   329          * @param hash  the hash code calculated for the entry key
       
   330          * @param key   the entry key
       
   331          * @param value the initial value of the entry
       
   332          * @param next  the next entry in a chain
       
   333          */
       
   334         private CacheEntry(int hash, K key, V value, CacheEntry<K,V> next) {
       
   335             this.hash = hash;
       
   336             this.key = Cache.this.keyKind.create(this, key, Cache.this.queue);
       
   337             this.value = Cache.this.valueKind.create(this, value, Cache.this.queue);
       
   338             this.next = next;
       
   339         }
       
   340 
       
   341         /**
       
   342          * Determines whether the entry has the given key with the given hash code.
       
   343          *
       
   344          * @param hash   an expected hash code
       
   345          * @param object an object to be compared with the entry key
       
   346          * @return {@code true} if the entry has the given key with the given hash code;
       
   347          *         {@code false} otherwise
       
   348          */
       
   349         private boolean matches(int hash, Object object) {
       
   350             if (this.hash != hash) {
       
   351                 return false;
       
   352             }
       
   353             Object key = this.key.getReferent();
       
   354             return (key == object) || !Cache.this.identity && (key != null) && key.equals(object);
       
   355         }
       
   356 
       
   357         /**
       
   358          * Marks the entry as actually removed from the cache.
       
   359          */
       
   360         private void unlink() {
       
   361             this.next = null;
       
   362             this.key.removeOwner();
       
   363             this.value.removeOwner();
       
   364             Cache.this.size--;
       
   365         }
       
   366     }
       
   367 
       
   368     /**
       
   369      * Basic interface for references.
       
   370      * It defines the operations common for the all kind of references.
       
   371      *
       
   372      * @param <T> the type of object to refer
       
   373      */
       
   374     private static interface Ref<T> {
       
   375         /**
       
   376          * Returns the object that possesses information about the reference.
       
   377          *
       
   378          * @return the owner of the reference or {@code null} if the owner is unknown
       
   379          */
       
   380         Object getOwner();
       
   381 
       
   382         /**
       
   383          * Returns the object to refer.
       
   384          *
       
   385          * @return the referred object or {@code null} if it was collected
       
   386          */
       
   387         T getReferent();
       
   388 
       
   389         /**
       
   390          * Determines whether the referred object was taken by the garbage collector or not.
       
   391          *
       
   392          * @return {@code true} if the referred object was collected
       
   393          */
       
   394         boolean isStale();
       
   395 
       
   396         /**
       
   397          * Marks this reference as removed from the cache.
       
   398          */
       
   399         void removeOwner();
       
   400     }
       
   401 
       
   402     /**
       
   403      * Represents a reference kind.
       
   404      */
       
   405     public static enum Kind {
       
   406         STRONG {
       
   407             <T> Ref<T> create(Object owner, T value, ReferenceQueue<? super T> queue) {
       
   408                 return new Strong<>(owner, value);
       
   409             }
       
   410         },
       
   411         SOFT {
       
   412             <T> Ref<T> create(Object owner, T referent, ReferenceQueue<? super T> queue) {
       
   413                 return (referent == null)
       
   414                         ? new Strong<>(owner, referent)
       
   415                         : new Soft<>(owner, referent, queue);
       
   416             }
       
   417         },
       
   418         WEAK {
       
   419             <T> Ref<T> create(Object owner, T referent, ReferenceQueue<? super T> queue) {
       
   420                 return (referent == null)
       
   421                         ? new Strong<>(owner, referent)
       
   422                         : new Weak<>(owner, referent, queue);
       
   423             }
       
   424         };
       
   425 
       
   426         /**
       
   427          * Creates a reference to the specified object.
       
   428          *
       
   429          * @param <T>      the type of object to refer
       
   430          * @param owner    the owner of the reference, if needed
       
   431          * @param referent the object to refer
       
   432          * @param queue    the queue to register the reference with,
       
   433          *                 or {@code null} if registration is not required
       
   434          * @return the reference to the specified object
       
   435          */
       
   436         abstract <T> Ref<T> create(Object owner, T referent, ReferenceQueue<? super T> queue);
       
   437 
       
   438         /**
       
   439          * This is an implementation of the {@link Cache.Ref} interface
       
   440          * that uses the strong references that prevent their referents
       
   441          * from being made finalizable, finalized, and then reclaimed.
       
   442          *
       
   443          * @param <T> the type of object to refer
       
   444          */
       
   445         private static final class Strong<T> implements Ref<T> {
       
   446             private Object owner;
       
   447             private final T referent;
       
   448 
       
   449             /**
       
   450              * Creates a strong reference to the specified object.
       
   451              *
       
   452              * @param owner    the owner of the reference, if needed
       
   453              * @param referent the non-null object to refer
       
   454              */
       
   455             private Strong(Object owner, T referent) {
       
   456                 this.owner = owner;
       
   457                 this.referent = referent;
       
   458             }
       
   459 
       
   460             /**
       
   461              * Returns the object that possesses information about the reference.
       
   462              *
       
   463              * @return the owner of the reference or {@code null} if the owner is unknown
       
   464              */
       
   465             public Object getOwner() {
       
   466                 return this.owner;
       
   467             }
       
   468 
       
   469             /**
       
   470              * Returns the object to refer.
       
   471              *
       
   472              * @return the referred object
       
   473              */
       
   474             public T getReferent() {
       
   475                 return this.referent;
       
   476             }
       
   477 
       
   478             /**
       
   479              * Determines whether the referred object was taken by the garbage collector or not.
       
   480              *
       
   481              * @return {@code true} if the referred object was collected
       
   482              */
       
   483             public boolean isStale() {
       
   484                 return false;
       
   485             }
       
   486 
       
   487             /**
       
   488              * Marks this reference as removed from the cache.
       
   489              */
       
   490             public void removeOwner() {
       
   491                 this.owner = null;
       
   492             }
       
   493         }
       
   494 
       
   495         /**
       
   496          * This is an implementation of the {@link Cache.Ref} interface
       
   497          * that uses the soft references that are cleared at the discretion
       
   498          * of the garbage collector in response to a memory request.
       
   499          *
       
   500          * @param <T> the type of object to refer
       
   501          * @see java.lang.ref.SoftReference
       
   502          */
       
   503         private static final class Soft<T> extends SoftReference<T> implements Ref<T> {
       
   504             private Object owner;
       
   505 
       
   506             /**
       
   507              * Creates a soft reference to the specified object.
       
   508              *
       
   509              * @param owner    the owner of the reference, if needed
       
   510              * @param referent the non-null object to refer
       
   511              * @param queue    the queue to register the reference with,
       
   512              *                 or {@code null} if registration is not required
       
   513              */
       
   514             private Soft(Object owner, T referent, ReferenceQueue<? super T> queue) {
       
   515                 super(referent, queue);
       
   516                 this.owner = owner;
       
   517             }
       
   518 
       
   519             /**
       
   520              * Returns the object that possesses information about the reference.
       
   521              *
       
   522              * @return the owner of the reference or {@code null} if the owner is unknown
       
   523              */
       
   524             public Object getOwner() {
       
   525                 return this.owner;
       
   526             }
       
   527 
       
   528             /**
       
   529              * Returns the object to refer.
       
   530              *
       
   531              * @return the referred object or {@code null} if it was collected
       
   532              */
       
   533             public T getReferent() {
       
   534                 return get();
       
   535             }
       
   536 
       
   537             /**
       
   538              * Determines whether the referred object was taken by the garbage collector or not.
       
   539              *
       
   540              * @return {@code true} if the referred object was collected
       
   541              */
       
   542             public boolean isStale() {
       
   543                 return null == get();
       
   544             }
       
   545 
       
   546             /**
       
   547              * Marks this reference as removed from the cache.
       
   548              */
       
   549             public void removeOwner() {
       
   550                 this.owner = null;
       
   551             }
       
   552         }
       
   553 
       
   554         /**
       
   555          * This is an implementation of the {@link Cache.Ref} interface
       
   556          * that uses the weak references that do not prevent their referents
       
   557          * from being made finalizable, finalized, and then reclaimed.
       
   558          *
       
   559          * @param <T> the type of object to refer
       
   560          * @see java.lang.ref.WeakReference
       
   561          */
       
   562         private static final class Weak<T> extends WeakReference<T> implements Ref<T> {
       
   563             private Object owner;
       
   564 
       
   565             /**
       
   566              * Creates a weak reference to the specified object.
       
   567              *
       
   568              * @param owner    the owner of the reference, if needed
       
   569              * @param referent the non-null object to refer
       
   570              * @param queue    the queue to register the reference with,
       
   571              *                 or {@code null} if registration is not required
       
   572              */
       
   573             private Weak(Object owner, T referent, ReferenceQueue<? super T> queue) {
       
   574                 super(referent, queue);
       
   575                 this.owner = owner;
       
   576             }
       
   577 
       
   578             /**
       
   579              * Returns the object that possesses information about the reference.
       
   580              *
       
   581              * @return the owner of the reference or {@code null} if the owner is unknown
       
   582              */
       
   583             public Object getOwner() {
       
   584                 return this.owner;
       
   585             }
       
   586 
       
   587             /**
       
   588              * Returns the object to refer.
       
   589              *
       
   590              * @return the referred object or {@code null} if it was collected
       
   591              */
       
   592             public T getReferent() {
       
   593                 return get();
       
   594             }
       
   595 
       
   596             /**
       
   597              * Determines whether the referred object was taken by the garbage collector or not.
       
   598              *
       
   599              * @return {@code true} if the referred object was collected
       
   600              */
       
   601             public boolean isStale() {
       
   602                 return null == get();
       
   603             }
       
   604 
       
   605             /**
       
   606              * Marks this reference as removed from the cache.
       
   607              */
       
   608             public void removeOwner() {
       
   609                 this.owner = null;
       
   610             }
       
   611         }
       
   612     }
       
   613 }