Skip to content

Instantly share code, notes, and snippets.

@edalquist
Created February 21, 2012 15:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edalquist/1877140 to your computer and use it in GitHub Desktop.
Save edalquist/1877140 to your computer and use it in GitHub Desktop.
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