SoftReferences should be deleted automatically. #777

pull/787/head
Nikita 8 years ago
parent 07ae51add5
commit 57aea881c5

@ -38,7 +38,7 @@ import io.netty.util.internal.PlatformDependent;
public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
final int size;
final ConcurrentMap<K, CachedValue> map = PlatformDependent.newConcurrentHashMap();
final ConcurrentMap<K, CachedValue<K, V>> map = PlatformDependent.newConcurrentHashMap();
private final long timeToLiveInMillis;
private final long maxIdleInMillis;
@ -52,11 +52,11 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
this.timeToLiveInMillis = timeToLiveInMillis;
}
protected void onValueRead(CachedValue value) {
protected void onValueRead(CachedValue<K, V> value) {
}
protected void onValueRemove(CachedValue value) {
protected void onValueRemove(CachedValue<K, V> value) {
}
@ -89,7 +89,7 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
throw new NullPointerException();
}
CachedValue entry = map.get(key);
CachedValue<K, V> entry = map.get(key);
if (entry == null) {
return false;
}
@ -113,8 +113,8 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
throw new NullPointerException();
}
for (Map.Entry<K, CachedValue> entry : map.entrySet()) {
CachedValue cachedValue = entry.getValue();
for (Map.Entry<K, CachedValue<K, V>> entry : map.entrySet()) {
CachedValue<K, V> cachedValue = entry.getValue();
if (cachedValue.getValue().equals(value)) {
if (cachedValue.isExpired()) {
if (map.remove(cachedValue.getKey(), cachedValue)) {
@ -139,7 +139,7 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
throw new NullPointerException();
}
CachedValue entry = map.get(key);
CachedValue<K, V> entry = map.get(key);
if (entry == null) {
return null;
}
@ -153,8 +153,7 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
return readValue(entry);
}
@SuppressWarnings("unchecked")
protected V readValue(CachedValue entry) {
protected V readValue(CachedValue<K, V> entry) {
onValueRead(entry);
return (V) entry.getValue();
}
@ -168,17 +167,16 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
return put(key, value, timeToLiveInMillis, TimeUnit.MILLISECONDS, maxIdleInMillis, TimeUnit.MILLISECONDS);
}
@SuppressWarnings("unchecked")
@Override
public V put(K key, V value, long ttl, TimeUnit ttlUnit, long maxIdleTime, TimeUnit maxIdleUnit) {
CachedValue entry = create(key, value, ttlUnit.toMillis(ttl), maxIdleUnit.toMillis(maxIdleTime));
CachedValue<K, V> entry = create(key, value, ttlUnit.toMillis(ttl), maxIdleUnit.toMillis(maxIdleTime));
if (isFull(key)) {
if (!removeExpiredEntries()) {
onMapFull();
}
}
onValueCreate(entry);
CachedValue prevCachedValue = map.put(key, entry);
CachedValue<K, V> prevCachedValue = map.put(key, entry);
if (prevCachedValue != null) {
onValueRemove(prevCachedValue);
if (!prevCachedValue.isExpired()) {
@ -188,17 +186,17 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
return null;
}
protected CachedValue create(K key, V value, long ttl, long maxIdleTime) {
return new StdCachedValue(key, value, ttl, maxIdleTime);
protected CachedValue<K, V> create(K key, V value, long ttl, long maxIdleTime) {
return new StdCachedValue<K, V>(key, value, ttl, maxIdleTime);
}
protected void onValueCreate(CachedValue entry) {
protected void onValueCreate(CachedValue<K, V> entry) {
}
private boolean removeExpiredEntries() {
protected boolean removeExpiredEntries() {
boolean removed = false;
// TODO optimize
for (CachedValue value : map.values()) {
for (CachedValue<K, V> value : map.values()) {
if (value.isExpired()) {
if (map.remove(value.getKey(), value)) {
onValueRemove(value);
@ -232,10 +230,9 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
* (non-Javadoc)
* @see java.util.Map#remove(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public V remove(Object key) {
CachedValue entry = map.remove(key);
CachedValue<K, V> entry = map.remove(key);
if (entry != null) {
onValueRemove(entry);
if (!entry.isExpired()) {
@ -298,9 +295,9 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
abstract class MapIterator<M> implements Iterator<M> {
final Iterator<Map.Entry<K, CachedValue>> keyIterator = map.entrySet().iterator();
final Iterator<Map.Entry<K, CachedValue<K, V>>> keyIterator = map.entrySet().iterator();
Map.Entry<K, CachedValue> mapEntry;
Map.Entry<K, CachedValue<K, V>> mapEntry;
@Override
public boolean hasNext() {
@ -309,7 +306,7 @@ public abstract class AbstractCacheMap<K, V> implements Cache<K, V> {
}
mapEntry = null;
while (keyIterator.hasNext()) {
Map.Entry<K, CachedValue> entry = keyIterator.next();
Map.Entry<K, CachedValue<K, V>> entry = keyIterator.next();
if (entry.getValue().isExpired()) {
continue;
}

@ -0,0 +1,34 @@
/**
* Copyright 2016 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.redisson.cache;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class CachedValueReference<V> extends SoftReference<V> {
private final CachedValue<?, ?> owner;
public CachedValueReference(CachedValue<?, ?> owner, V referent, ReferenceQueue<? super V> q) {
super(referent, q);
this.owner = owner;
}
public CachedValue<?, ?> getOwner() {
return owner;
}
}

@ -15,6 +15,8 @@
*/
package org.redisson.cache;
import java.lang.ref.ReferenceQueue;
/**
*
* @author Nikita Koksharov
@ -24,14 +26,28 @@ package org.redisson.cache;
*/
public class SoftCacheMap<K, V> extends AbstractCacheMap<K, V> {
private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
public SoftCacheMap(long timeToLiveInMillis, long maxIdleInMillis) {
super(0, timeToLiveInMillis, maxIdleInMillis);
}
protected CachedValue create(K key, V value, long ttl, long maxIdleTime) {
return new SoftCachedValue(key, value, ttl, maxIdleTime);
protected CachedValue<K, V> create(K key, V value, long ttl, long maxIdleTime) {
return new SoftCachedValue<K, V>(key, value, ttl, maxIdleTime, queue);
}
@Override
protected boolean removeExpiredEntries() {
while (true) {
CachedValueReference<V> value = (CachedValueReference<V>) queue.poll();
if (value == null) {
break;
}
map.remove(value.getOwner().getKey(), value.getOwner());
}
return super.removeExpiredEntries();
}
@Override
protected void onMapFull() {
}

@ -15,36 +15,25 @@
*/
package org.redisson.cache;
import java.lang.ref.SoftReference;
import java.lang.ref.ReferenceQueue;
/**
* Created by jribble on 2/20/17.
*/
public class SoftCachedValue<K, V> implements CachedValue<K, V> {
StdCachedValue<K, SoftReference<V>> value;
public class SoftCachedValue<K, V> extends StdCachedValue<K, V> implements CachedValue<K, V> {
private final CachedValueReference<V> ref;
public SoftCachedValue(K key, V value, long ttl, long maxIdleTime) {
this.value = new StdCachedValue<K, SoftReference<V>>(key, new SoftReference<V>(value), ttl, maxIdleTime);
}
@Override
public boolean isExpired() {
return value.isExpired();
}
@Override
public K getKey() {
return value.getKey();
public SoftCachedValue(K key, V value, long ttl, long maxIdleTime, ReferenceQueue<V> queue) {
super(key, null, ttl, maxIdleTime);
this.ref = new CachedValueReference<V>(this, value, queue);
}
@Override
public V getValue() {
return value.getValue().get();
super.getValue();
return ref.get();
}
@Override
public String toString() {
return value.toString();
}
}

@ -62,6 +62,7 @@ public class SoftCacheMapTest {
assertThat(map.values().stream().filter(Objects::nonNull).count()).isEqualTo(100000);
System.gc();
assertThat(map.values().stream().filter(Objects::nonNull).count()).isZero();
assertThat(map.values().size()).isZero();
}
}

Loading…
Cancel
Save