mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix weak bug
This commit is contained in:
parent
477657ffb8
commit
af977ac3d4
@ -3,6 +3,8 @@ package cn.hutool.cache.impl;
|
|||||||
import cn.hutool.cache.Cache;
|
import cn.hutool.cache.Cache;
|
||||||
import cn.hutool.cache.CacheListener;
|
import cn.hutool.cache.CacheListener;
|
||||||
import cn.hutool.core.lang.func.Func0;
|
import cn.hutool.core.lang.func.Func0;
|
||||||
|
import cn.hutool.core.lang.mutable.Mutable;
|
||||||
|
import cn.hutool.core.lang.mutable.MutableObj;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -11,6 +13,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.atomic.LongAdder;
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 超时和限制大小的缓存的默认实现<br>
|
* 超时和限制大小的缓存的默认实现<br>
|
||||||
@ -27,7 +30,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
protected Map<K, CacheObj<K, V>> cacheMap;
|
protected Map<Mutable<K>, CacheObj<K, V>> cacheMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 写的时候每个key一把锁,降低锁的粒度
|
* 写的时候每个key一把锁,降低锁的粒度
|
||||||
@ -84,7 +87,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
if (isFull()) {
|
if (isFull()) {
|
||||||
pruneCache();
|
pruneCache();
|
||||||
}
|
}
|
||||||
cacheMap.put(key, co);
|
cacheMap.put(MutableObj.of(key), co);
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------------- put end
|
// ---------------------------------------------------------------- put end
|
||||||
|
|
||||||
@ -112,7 +115,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
keyLock.lock();
|
keyLock.lock();
|
||||||
try {
|
try {
|
||||||
// 双重检查锁,防止在竞争锁的过程中已经有其它线程写入
|
// 双重检查锁,防止在竞争锁的过程中已经有其它线程写入
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
final CacheObj<K, V> co = getWithoutLock(key);
|
||||||
if (null == co || co.isExpired()) {
|
if (null == co || co.isExpired()) {
|
||||||
try {
|
try {
|
||||||
v = supplier.call();
|
v = supplier.call();
|
||||||
@ -130,6 +133,16 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取键对应的{@link CacheObj}
|
||||||
|
* @param key 键,实际使用时会被包装为{@link MutableObj}
|
||||||
|
* @return {@link CacheObj}
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
protected CacheObj<K, V> getWithoutLock(K key){
|
||||||
|
return this.cacheMap.get(MutableObj.of(key));
|
||||||
|
}
|
||||||
// ---------------------------------------------------------------- get end
|
// ---------------------------------------------------------------- get end
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -212,7 +225,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
* @since 5.5.9
|
* @since 5.5.9
|
||||||
*/
|
*/
|
||||||
public Set<K> keySet(){
|
public Set<K> keySet(){
|
||||||
return this.cacheMap.keySet();
|
return this.cacheMap.keySet().stream().map(Mutable::get).collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,11 +250,20 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
* @return 移除的对象,无返回null
|
* @return 移除的对象,无返回null
|
||||||
*/
|
*/
|
||||||
protected CacheObj<K, V> removeWithoutLock(K key, boolean withMissCount) {
|
protected CacheObj<K, V> removeWithoutLock(K key, boolean withMissCount) {
|
||||||
final CacheObj<K, V> co = cacheMap.remove(key);
|
final CacheObj<K, V> co = cacheMap.remove(MutableObj.of(key));
|
||||||
if (withMissCount) {
|
if (withMissCount) {
|
||||||
// 在丢失计数有效的情况下,移除一般为get时的超时操作,此处应该丢失数+1
|
// 在丢失计数有效的情况下,移除一般为get时的超时操作,此处应该丢失数+1
|
||||||
this.missCount.increment();
|
this.missCount.increment();
|
||||||
}
|
}
|
||||||
return co;
|
return co;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有{@link CacheObj}值的{@link Iterator}形式
|
||||||
|
* @return {@link Iterator}
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
protected Iterator<CacheObj<K, V>> cacheObjIter(){
|
||||||
|
return this.cacheMap.values().iterator();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ public class FIFOCache<K, V> extends StampedCache<K, V> {
|
|||||||
CacheObj<K, V> first = null;
|
CacheObj<K, V> first = null;
|
||||||
|
|
||||||
// 清理过期对象并找出链表头部元素(先入元素)
|
// 清理过期对象并找出链表头部元素(先入元素)
|
||||||
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
|
final Iterator<CacheObj<K, V>> values = cacheObjIter();
|
||||||
if (isPruneExpiredActive()) {
|
if (isPruneExpiredActive()) {
|
||||||
// 清理过期对象并找出链表头部元素(先入元素)
|
// 清理过期对象并找出链表头部元素(先入元素)
|
||||||
while (values.hasNext()) {
|
while (values.hasNext()) {
|
||||||
@ -71,7 +71,7 @@ public class FIFOCache<K, V> extends StampedCache<K, V> {
|
|||||||
|
|
||||||
// 清理结束后依旧是满的,则删除第一个被缓存的对象
|
// 清理结束后依旧是满的,则删除第一个被缓存的对象
|
||||||
if (isFull() && null != first) {
|
if (isFull() && null != first) {
|
||||||
cacheMap.remove(first.key);
|
removeWithoutLock(first.key, false);
|
||||||
onRemove(first.key, first.obj);
|
onRemove(first.key, first.obj);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ public class LFUCache<K, V> extends StampedCache<K, V> {
|
|||||||
CacheObj<K, V> comin = null;
|
CacheObj<K, V> comin = null;
|
||||||
|
|
||||||
// 清理过期对象并找出访问最少的对象
|
// 清理过期对象并找出访问最少的对象
|
||||||
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
|
Iterator<CacheObj<K, V>> values = cacheObjIter();
|
||||||
CacheObj<K, V> co;
|
CacheObj<K, V> co;
|
||||||
while (values.hasNext()) {
|
while (values.hasNext()) {
|
||||||
co = values.next();
|
co = values.next();
|
||||||
@ -78,7 +78,7 @@ public class LFUCache<K, V> extends StampedCache<K, V> {
|
|||||||
if (isFull() && comin != null) {
|
if (isFull() && comin != null) {
|
||||||
long minAccessCount = comin.accessCount.get();
|
long minAccessCount = comin.accessCount.get();
|
||||||
|
|
||||||
values = cacheMap.values().iterator();
|
values = cacheObjIter();
|
||||||
CacheObj<K, V> co1;
|
CacheObj<K, V> co1;
|
||||||
while (values.hasNext()) {
|
while (values.hasNext()) {
|
||||||
co1 = values.next();
|
co1 = values.next();
|
||||||
|
@ -56,7 +56,7 @@ public class LRUCache<K, V> extends ReentrantCache<K, V> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
|
Iterator<CacheObj<K, V>> values = cacheObjIter();
|
||||||
CacheObj<K, V> co;
|
CacheObj<K, V> co;
|
||||||
while (values.hasNext()) {
|
while (values.hasNext()) {
|
||||||
co = values.next();
|
co = values.next();
|
||||||
|
@ -36,7 +36,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
|
|||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
// 不存在或已移除
|
// 不存在或已移除
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
final CacheObj<K, V> co = getWithoutLock(key);
|
||||||
if (co == null) {
|
if (co == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
|
|||||||
CacheObj<K, V> co;
|
CacheObj<K, V> co;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
co = cacheMap.get(key);
|
co = getWithoutLock(key);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
|
|||||||
CopiedIter<CacheObj<K, V>> copiedIterator;
|
CopiedIter<CacheObj<K, V>> copiedIterator;
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
|
copiedIterator = CopiedIter.copyOf(cacheObjIter());
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
|
|||||||
final long stamp = lock.readLock();
|
final long stamp = lock.readLock();
|
||||||
try {
|
try {
|
||||||
// 不存在或已移除
|
// 不存在或已移除
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
final CacheObj<K, V> co = getWithoutLock(key);
|
||||||
if (co == null) {
|
if (co == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -58,12 +58,12 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
|
|||||||
public V get(K key, boolean isUpdateLastAccess) {
|
public V get(K key, boolean isUpdateLastAccess) {
|
||||||
// 尝试读取缓存,使用乐观读锁
|
// 尝试读取缓存,使用乐观读锁
|
||||||
long stamp = lock.tryOptimisticRead();
|
long stamp = lock.tryOptimisticRead();
|
||||||
CacheObj<K, V> co = cacheMap.get(key);
|
CacheObj<K, V> co = getWithoutLock(key);
|
||||||
if(false == lock.validate(stamp)){
|
if(false == lock.validate(stamp)){
|
||||||
// 有写线程修改了此对象,悲观读
|
// 有写线程修改了此对象,悲观读
|
||||||
stamp = lock.readLock();
|
stamp = lock.readLock();
|
||||||
try {
|
try {
|
||||||
co = cacheMap.get(key);
|
co = getWithoutLock(key);
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlockRead(stamp);
|
lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V>{
|
|||||||
CopiedIter<CacheObj<K, V>> copiedIterator;
|
CopiedIter<CacheObj<K, V>> copiedIterator;
|
||||||
final long stamp = lock.readLock();
|
final long stamp = lock.readLock();
|
||||||
try {
|
try {
|
||||||
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
|
copiedIterator = CopiedIter.copyOf(cacheObjIter());
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlockRead(stamp);
|
lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.hutool.cache.impl;
|
package cn.hutool.cache.impl;
|
||||||
|
|
||||||
import cn.hutool.cache.GlobalPruneTimer;
|
import cn.hutool.cache.GlobalPruneTimer;
|
||||||
|
import cn.hutool.core.lang.mutable.Mutable;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -37,7 +38,7 @@ public class TimedCache<K, V> extends StampedCache<K, V> {
|
|||||||
* @param timeout 过期时长
|
* @param timeout 过期时长
|
||||||
* @param map 存储缓存对象的map
|
* @param map 存储缓存对象的map
|
||||||
*/
|
*/
|
||||||
public TimedCache(long timeout, Map<K, CacheObj<K, V>> map) {
|
public TimedCache(long timeout, Map<Mutable<K>, CacheObj<K, V>> map) {
|
||||||
this.capacity = 0;
|
this.capacity = 0;
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
this.cacheMap = map;
|
this.cacheMap = map;
|
||||||
@ -52,7 +53,7 @@ public class TimedCache<K, V> extends StampedCache<K, V> {
|
|||||||
@Override
|
@Override
|
||||||
protected int pruneCache() {
|
protected int pruneCache() {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Iterator<CacheObj<K, V>> values = cacheMap.values().iterator();
|
final Iterator<CacheObj<K, V>> values = cacheObjIter();
|
||||||
CacheObj<K, V> co;
|
CacheObj<K, V> co;
|
||||||
while (values.hasNext()) {
|
while (values.hasNext()) {
|
||||||
co = values.next();
|
co = values.next();
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
package cn.hutool.cache.impl;
|
package cn.hutool.cache.impl;
|
||||||
|
|
||||||
import cn.hutool.cache.Cache;
|
|
||||||
import cn.hutool.core.lang.func.Func0;
|
|
||||||
import cn.hutool.core.lang.mutable.MutableObj;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,112 +7,21 @@ import java.util.WeakHashMap;
|
|||||||
* 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。<br>
|
* 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。<br>
|
||||||
* 丢弃某个键时,其条目从映射中有效地移除。<br>
|
* 丢弃某个键时,其条目从映射中有效地移除。<br>
|
||||||
*
|
*
|
||||||
* @param <K> 键类型
|
|
||||||
* @param <V> 值类型
|
|
||||||
* @author Looly
|
* @author Looly
|
||||||
|
*
|
||||||
|
* @param <K> 键
|
||||||
|
* @param <V> 值
|
||||||
|
* @author looly
|
||||||
* @since 3.0.7
|
* @since 3.0.7
|
||||||
*/
|
*/
|
||||||
public class WeakCache<K, V> implements Cache<K, V> {
|
public class WeakCache<K, V> extends TimedCache<K, V>{
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
TimedCache<MutableObj<K>, V> timedCache;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
* @param timeout 超时时常,单位毫秒,-1或0表示无限制
|
||||||
* @param timeout 超时
|
|
||||||
*/
|
*/
|
||||||
public WeakCache(long timeout) {
|
public WeakCache(long timeout) {
|
||||||
this.timedCache = new TimedCache<>(timeout, new WeakHashMap<>());
|
super(timeout, new WeakHashMap<>());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int capacity() {
|
|
||||||
return timedCache.capacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long timeout() {
|
|
||||||
return timedCache.timeout();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(K key, V object) {
|
|
||||||
timedCache.put(new MutableObj<>(key), object);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(K key, V object, long timeout) {
|
|
||||||
timedCache.put(new MutableObj<>(key), object, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) {
|
|
||||||
return timedCache.get(new MutableObj<>(key), isUpdateLastAccess, supplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(K key, boolean isUpdateLastAccess) {
|
|
||||||
return timedCache.get(new MutableObj<>(key), isUpdateLastAccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<CacheObj<K, V>> cacheObjIterator() {
|
|
||||||
final Iterator<CacheObj<MutableObj<K>, V>> timedIter = timedCache.cacheObjIterator();
|
|
||||||
return new Iterator<CacheObj<K, V>>() {
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return timedIter.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CacheObj<K, V> next() {
|
|
||||||
final CacheObj<MutableObj<K>, V> next = timedIter.next();
|
|
||||||
final CacheObj<K, V> nextNew = new CacheObj<>(next.key.get(), next.obj, next.ttl);
|
|
||||||
nextNew.lastAccess = next.lastAccess;
|
|
||||||
nextNew.accessCount = next.accessCount;
|
|
||||||
return nextNew;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int prune() {
|
|
||||||
return timedCache.prune();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFull() {
|
|
||||||
return timedCache.isFull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove(K key) {
|
|
||||||
timedCache.remove(new MutableObj<>(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
timedCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return timedCache.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return timedCache.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsKey(K key) {
|
|
||||||
return timedCache.containsKey(new MutableObj<>(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<V> iterator() {
|
|
||||||
return timedCache.iterator();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.hutool.cache;
|
package cn.hutool.cache;
|
||||||
|
|
||||||
import cn.hutool.cache.impl.WeakCache;
|
import cn.hutool.cache.impl.WeakCache;
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -14,8 +15,31 @@ public class WeakCacheTest {
|
|||||||
|
|
||||||
Assert.assertEquals(2, cache.size());
|
Assert.assertEquals(2, cache.size());
|
||||||
|
|
||||||
|
// 检查被MutableObj包装的key能否正常移除
|
||||||
cache.remove("abc");
|
cache.remove("abc");
|
||||||
|
|
||||||
Assert.assertEquals(1, cache.size());
|
Assert.assertEquals(1, cache.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeByGcTest(){
|
||||||
|
WeakCache<String, String> cache = new WeakCache<>(-1);
|
||||||
|
cache.put("a", "1");
|
||||||
|
cache.put("b", "2");
|
||||||
|
Assert.assertEquals(2, cache.size());
|
||||||
|
|
||||||
|
// GC测试
|
||||||
|
int i=0;
|
||||||
|
while(true){
|
||||||
|
if(2 == cache.size()){
|
||||||
|
i++;
|
||||||
|
Console.log("Object is alive for {} loops - ", i);
|
||||||
|
System.gc();
|
||||||
|
}else{
|
||||||
|
Console.log("Object has been collected.");
|
||||||
|
Console.log(cache.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,17 @@ import java.io.Serializable;
|
|||||||
public class MutableObj<T> implements Mutable<T>, Serializable {
|
public class MutableObj<T> implements Mutable<T>, Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建MutableObj
|
||||||
|
* @param value 被包装的值
|
||||||
|
* @param <T> 值类型
|
||||||
|
* @return MutableObj
|
||||||
|
* @since 5.8.0
|
||||||
|
*/
|
||||||
|
public static <T> MutableObj<T> of(T value){
|
||||||
|
return new MutableObj<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
private T value;
|
private T value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package cn.hutool.core.util;
|
package cn.hutool.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Console;
|
||||||
|
import cn.hutool.core.lang.mutable.MutableObj;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.lang.ref.PhantomReference;
|
import java.lang.ref.PhantomReference;
|
||||||
@ -31,4 +34,23 @@ public class ReferenceUtilTest {
|
|||||||
// get方法永远都返回null,PhantomReference只能用来监控对象的GC状况
|
// get方法永远都返回null,PhantomReference只能用来监控对象的GC状况
|
||||||
Assert.assertNull(integerReference.get());
|
Assert.assertNull(integerReference.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void gcTest(){
|
||||||
|
// https://blog.csdn.net/zmx729618/article/details/54093532
|
||||||
|
// 弱引用的对象必须使用可变对象,不能使用常量对象(比如String)
|
||||||
|
WeakReference<MutableObj<String>> reference = new WeakReference<>(new MutableObj<>("abc"));
|
||||||
|
int i=0;
|
||||||
|
while(true){
|
||||||
|
if(reference.get()!=null){
|
||||||
|
i++;
|
||||||
|
Console.log("Object is alive for {} loops - ", i);
|
||||||
|
System.gc();
|
||||||
|
}else{
|
||||||
|
Console.log("Object has been collected.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user