Created
February 21, 2012 15:59
-
-
Save edalquist/1877140 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/build.gradle b/build.gradle | |
index 73116a5..a7dbb41 100644 | |
--- a/build.gradle | |
+++ b/build.gradle | |
@@ -66,6 +66,9 @@ libraries = [ | |
// Jakarta commons-collections todo : get rid of commons-collections dependency | |
commons_collections: | |
'commons-collections:commons-collections:3.2.1', | |
+ // Google Guava | |
+ guava: | |
+ 'com.google.guava:guava:11.0.1@jar', | |
// Dom4J | |
dom4j: 'dom4j:dom4j:1.6.1@jar', | |
diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle | |
index 99a98e8..f52b6df 100644 | |
--- a/hibernate-core/hibernate-core.gradle | |
+++ b/hibernate-core/hibernate-core.gradle | |
@@ -5,6 +5,7 @@ apply plugin: org.hibernate.build.gradle.testing.matrix.MatrixTestingPlugin | |
dependencies { | |
compile( libraries.commons_collections ) | |
+ compile( libraries.guava ) | |
compile( libraries.jta ) | |
compile( libraries.dom4j ) { | |
transitive = false | |
diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/SoftLimitMRUCache.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/SoftLimitMRUCache.java | |
index 624dce8..bd27685 100644 | |
--- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/SoftLimitMRUCache.java | |
+++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/SoftLimitMRUCache.java | |
@@ -25,8 +25,11 @@ package org.hibernate.internal.util.collections; | |
import java.io.IOException; | |
import java.io.Serializable; | |
-import java.lang.ref.ReferenceQueue; | |
-import java.lang.ref.SoftReference; | |
+import java.util.concurrent.ConcurrentMap; | |
+ | |
+import com.google.common.cache.CacheBuilder; | |
+import com.google.common.cache.RemovalListener; | |
+import com.google.common.cache.RemovalNotification; | |
/** | |
* Cache following a "Most Recently Used" (MRU) algorithm for maintaining a | |
@@ -57,6 +60,9 @@ import java.lang.ref.SoftReference; | |
* <p/> | |
* <strong>Note:</strong> This class is serializable, however all entries are | |
* discarded on serialization. | |
+ * <p/> | |
+ * See http://guava-libraries.googlecode.com/files/ConcurrentCachingAtGoogle.pdf for details on | |
+ * eviction strategy | |
* | |
* @see org.hibernate.cfg.Environment#QUERY_PLAN_CACHE_MAX_STRONG_REFERENCES | |
* @see org.hibernate.cfg.Environment#QUERY_PLAN_CACHE_MAX_SOFT_REFERENCES | |
@@ -64,7 +70,7 @@ import java.lang.ref.SoftReference; | |
* @author Steve Ebersole | |
* @author Manuel Dominguez Sarmiento | |
*/ | |
-public class SoftLimitMRUCache implements Serializable { | |
+public class SoftLimitMRUCache<K, V> implements Serializable { | |
/** | |
* The default strong reference count. | |
*/ | |
@@ -78,9 +84,8 @@ public class SoftLimitMRUCache implements Serializable { | |
private final int strongRefCount; | |
private final int softRefCount; | |
- private transient LRUMap strongRefCache; | |
- private transient LRUMap softRefCache; | |
- private transient ReferenceQueue referenceQueue; | |
+ private transient ConcurrentMap<K, V> strongRefCache; | |
+ private transient ConcurrentMap<K, V> softRefCache; | |
/** | |
* Constructs a cache with the default settings. | |
@@ -121,21 +126,22 @@ public class SoftLimitMRUCache implements Serializable { | |
* | |
* @return the stored value, or <code>null</code> if no entry exists. | |
*/ | |
- public synchronized Object get(Object key) { | |
+ public synchronized V get(K key) { | |
if ( key == null ) { | |
throw new NullPointerException( "Key to get cannot be null" ); | |
} | |
+ | |
+ // first check strong ref cache, where most recently used entries should live | |
+ V entry = strongRefCache.get( key ); | |
+ if ( entry != null ) { | |
+ return entry; | |
+ } | |
- 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; | |
- } | |
+ // next check soft ref cache, do a remove since if the entry is found it will be moved to the strong ref cache | |
+ entry = softRefCache.remove( key ); | |
+ if ( entry != null ) { | |
+ strongRefCache.put( null, entry ); | |
+ return entry; | |
} | |
return null; | |
@@ -149,22 +155,20 @@ public class SoftLimitMRUCache implements Serializable { | |
* | |
* @return the previous value stored in the cache, if any. | |
*/ | |
- public synchronized Object put(Object key, Object value) { | |
+ public synchronized V put(K key, V 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 ) | |
- ); | |
+ | |
+ V prev = strongRefCache.put( key, value ); | |
+ if ( prev == null ) { | |
+ prev = softRefCache.remove( key ); | |
+ } | |
- return ( ref != null ) ? ref.get() : null; | |
+ return ( prev != null ) ? prev : null; | |
} | |
/** | |
@@ -173,7 +177,6 @@ public class SoftLimitMRUCache implements Serializable { | |
* @return the strong reference cache size. | |
*/ | |
public synchronized int size() { | |
- clearObsoleteReferences(); | |
return strongRefCache.size(); | |
} | |
@@ -183,7 +186,6 @@ public class SoftLimitMRUCache implements Serializable { | |
* @return the soft reference cache size. | |
*/ | |
public synchronized int softSize() { | |
- clearObsoleteReferences(); | |
return softRefCache.size(); | |
} | |
@@ -196,9 +198,20 @@ public class SoftLimitMRUCache implements Serializable { | |
} | |
private void init() { | |
- this.strongRefCache = new LRUMap( strongRefCount ); | |
- this.softRefCache = new LRUMap( softRefCount ); | |
- this.referenceQueue = new ReferenceQueue(); | |
+ this.softRefCache = CacheBuilder | |
+ .newBuilder() | |
+ .maximumSize(softRefCount) | |
+ .softValues() | |
+ .<K, V> build() | |
+ .asMap(); | |
+ | |
+ this.strongRefCache = CacheBuilder | |
+ .newBuilder() | |
+ .maximumSize( strongRefCount ) | |
+ .softValues() | |
+ .removalListener( new RedirectingRemovalListener<K, V>( this.softRefCache ) ) | |
+ .<K, V> build() | |
+ .asMap(); | |
} | |
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { | |
@@ -206,26 +219,18 @@ public class SoftLimitMRUCache implements Serializable { | |
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 RedirectingRemovalListener<RK, RV> implements RemovalListener<RK, RV> { | |
+ private final ConcurrentMap<RK, RV> targetMap; | |
+ | |
+ public RedirectingRemovalListener(ConcurrentMap<RK, RV> targetMap) { | |
+ this.targetMap = targetMap; | |
} | |
- } | |
- | |
- 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; | |
+ | |
+ @Override | |
+ public void onRemoval(RemovalNotification<RK, RV> notification) { | |
+ if (notification.wasEvicted()) { | |
+ targetMap.put( notification.getKey(), notification.getValue() ); | |
+ } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment