All Downloads are FREE. Search and download functionalities are using the official Maven repository.

panda.lang.collection.SoftLimitMRUCache Maven / Gradle / Ivy

package panda.lang.collection;
import java.io.Serializable;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;

/**
 * Cache following a "Most Recently Used" (MRU) algorithm for maintaining a
 * bounded in-memory size; the "Least Recently Used" (LRU) entry is the first
 * available for removal from the cache.
 * 

* This implementation uses a "soft limit" to the in-memory size of the cache, * meaning that all cache entries are kept within a completely * {@link java.lang.ref.SoftReference}-based map with the most recently utilized * entries additionally kept in a hard-reference manner to prevent those cache * entries soft references from becoming enqueued by the garbage collector. Thus * the actual size of this cache impl can actually grow beyond the stated max * size bound as long as GC is not actively seeking soft references for * enqueuement. *

* The soft-size is bounded and configurable. This allows controlling memory * usage which can grow out of control under some circumstances, especially when * very large heaps are in use. Although memory usage per se should not be a * problem with soft references, which are cleared when necessary, this can * trigger extremely slow stop-the-world GC pauses when nearing full heap usage, * even with CMS concurrent GC (i.e. concurrent mode failure). This is most * evident when ad-hoc HQL queries are produced by the application, leading to * poor soft-cache hit ratios. This can also occur with heavy use of SQL IN * clauses, which will generate multiples SQL queries (even if parameterized), * one for each collection/array size passed to the IN clause. Many slightly * different queries will eventually fill the heap and trigger a full GC to * reclaim space, leading to unacceptable pauses in some cases. *

* Note: This class is serializable, however all entries are * discarded on serialization. * * @author Steve Ebersole * @author Manuel Dominguez Sarmiento */ @SuppressWarnings("rawtypes") public class SoftLimitMRUCache implements Serializable { private static final long serialVersionUID = 1L; /** * The default strong reference count. */ public static final int DEFAULT_STRONG_REF_COUNT = 128; /** * The default soft reference count. */ public static final int DEFAULT_SOFT_REF_COUNT = 2048; private final int strongRefCount; private final int softRefCount; private transient LRUMap strongRefCache; private transient LRUMap softRefCache; private transient ReferenceQueue referenceQueue; /** * Constructs a cache with the default settings. * * @see #DEFAULT_STRONG_REF_COUNT * @see #DEFAULT_SOFT_REF_COUNT */ public SoftLimitMRUCache() { this( DEFAULT_STRONG_REF_COUNT, DEFAULT_SOFT_REF_COUNT ); } /** * Constructs a cache with the specified settings. * * @param strongRefCount the strong reference count. * @param softRefCount the soft reference count. * * @throws IllegalArgumentException if either of the arguments is less than one, or if the strong * reference count is higher than the soft reference count. */ public SoftLimitMRUCache(int strongRefCount, int softRefCount) { if ( strongRefCount < 1 || softRefCount < 1 ) { throw new IllegalArgumentException( "Reference counts must be greater than zero" ); } if ( strongRefCount > softRefCount ) { throw new IllegalArgumentException( "Strong reference count cannot exceed soft reference count" ); } this.strongRefCount = strongRefCount; this.softRefCount = softRefCount; init(); } /** * Gets an object from the cache. * * @param key the cache key. * * @return the stored value, or null if no entry exists. */ @SuppressWarnings("unchecked") public synchronized Object get(Object key) { if ( key == null ) { throw new NullPointerException( "Key to get cannot be null" ); } clearObsoleteReferences(); SoftReference ref = (SoftReference) softRefCache.get( key ); if ( ref != null ) { Object refValue = ref.get(); if ( refValue != null ) { // This ensures recently used entries are strongly-reachable strongRefCache.put( key, refValue ); return refValue; } } return null; } /** * Puts a value in the cache. * * @param key the key. * @param value the value. * * @return the previous value stored in the cache, if any. */ @SuppressWarnings("unchecked") public synchronized Object put(Object key, Object value) { if ( key == null || value == null ) { throw new NullPointerException( getClass().getName() + "does not support null key [" + key + "] or value [" + value + "]" ); } clearObsoleteReferences(); strongRefCache.put( key, value ); SoftReference ref = (SoftReference) softRefCache.put( key, new KeyedSoftReference( key, value, referenceQueue ) ); return ( ref != null ) ? ref.get() : null; } /** * Gets the strong reference cache size. * * @return the strong reference cache size. */ public synchronized int size() { clearObsoleteReferences(); return strongRefCache.size(); } /** * Gets the soft reference cache size. * * @return the soft reference cache size. */ public synchronized int softSize() { clearObsoleteReferences(); return softRefCache.size(); } /** * Clears the cache. */ public synchronized void clear() { strongRefCache.clear(); softRefCache.clear(); } private void init() { this.strongRefCache = new LRUMap( strongRefCount ); this.softRefCache = new LRUMap( softRefCount ); this.referenceQueue = new ReferenceQueue(); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); init(); } private void clearObsoleteReferences() { // Clear entries for soft references removed by garbage collector KeyedSoftReference obsoleteRef; while ( ( obsoleteRef = (KeyedSoftReference) referenceQueue.poll() ) != null ) { Object key = obsoleteRef.getKey(); softRefCache.remove( key ); } } private static class KeyedSoftReference extends SoftReference { private final Object key; @SuppressWarnings({ "unchecked" }) private KeyedSoftReference(Object key, Object value, ReferenceQueue q) { super( value, q ); this.key = key; } private Object getKey() { return key; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy