强引用(Strong Reference)是我们使用最多的一种对象引用,当一个对象被关键字 new 实例化出来的时候, JVM 会在堆(heap)内存中开辟一个内存区域,用于存放与该实例对应的数据结构。JVM 垃圾回收器线程会在达到 GC 条件的时候尝试回收(Full GC,Young GC)堆栈内存中的数据,强引用的特点是只要引用到 Root 根的路径可达,无论怎样的 GC 都不会将其释放,而是宁可出现 JVM 内存溢出。
Cache 是一种用于提高系统性能,提高数据检索效率的机制,而 LRU(Least recently used,最近最少使用)算法和 Cache 的结合是最常见的一种 Cache 实现。
LRU 是数据冷热治理的一种思想,不经常使用的数据被称为冷数据,经常使用的则被称为热数据,对冷数据分配提前释放,可以帮助我们节省更多的内存资源,LRUCache 的实现方式有很多种,在这里使用双向链表+hash表的方式来实现。
二 实战 1 定义被引用的对象package concurrent.lrucache.strongRefercence;
public class Reference {
// 1M
private final byte[] data = new byte[2 << 19];
@Override
protected void finalize() throws Throwable {
System.out.println("the reference will be GC");
}
}
2 最近最少使用缓存
package concurrent.lrucache.strongRefercence; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; public class LRUCache3 数据加载{ // 用于记录 key 值的顺序 private final LinkedList keyList = new LinkedList<>(); // 用于存放数据 private final Map cache = new HashMap<>(); // cache 的最大容量 private final int capacity; // 提供了一种加载数据的方式 private final CacheLoader cacheLoader; public LRUCache(int capacity, CacheLoader cacheLoader) { this.capacity = capacity; this.cacheLoader = cacheLoader; } public void put(K key, V value) { // 当元素数量超过容量时,将最老的数据清除 if (keyList.size() >= capacity) { K eldestkey = keyList.removeFirst(); // eldest data cache.remove(eldestkey); } // 如果数据已经存在,则从 key 的队列中删除 if (keyList.contains(key)) { keyList.remove(key); } // 将 key 存放到队尾 keyList.addLast(key); cache.put(key, value); } public V get(K key) { V value; // 先将 key 从 key list 中删除 boolean success = keyList.remove(key); // 如果删除失败则表明该数据不存在 if (!success) { // 通过 cacheLoader 对数据进行加载 value = cacheLoader.load(key); // 通过 put 方法 cache 数据 this.put(key, value); } else { // 如果删除成功,则从 cache 中返回数据,并且将 key 再次放到队尾 value = cache.get(key); keyList.addLast(key); } return value; } @Override public String toString() { return this.keyList.toString(); } }
package concurrent.lrucache.strongRefercence; @FunctionalInterface public interface CacheLoader4 测试{ // 定义加载数据的方法 V load(K k); }
package concurrent.lrucache.strongRefercence;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
test1();
test2();
}
private static void test1() {
LRUCache cache = new LRUCache<>(5, key -> new Reference());
cache.get("1");
cache.get("2");
cache.get("3");
cache.get("4");
cache.get("5");
cache.get("6");
System.out.println(cache.toString());
}
private static void test2() {
LRUCache cache1 = new LRUCache<>(200, key -> new Reference());
for (Integer i = 0; i < Integer.MAX_VALUE; i++) {
cache1.get(i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("The " + i + " reference stored at cache.");
}
}
}
三 test1 测试
1 测试结果
2 测试说明[2, 3, 4, 5, 6]
取缓存时,把最老的数据 1 给踢掉
四 test2 测试结果 1 启动参数2 测试结果-Xmx128M -Xms64M -XX:+PrintGCDetails
3 测试说明......
The 93 reference stored at cache.
The 94 reference stored at cache.
The 95 reference stored at cache.
The 96 reference stored at cache.
The 97 reference stored at cache.
The 98 reference stored at cache.
[Full GC (Ergonomics) [PSYoungGen: 15677K->15362K(18944K)] [ParOldGen: 87227K->87226K(87552K)] 102904K->102589K(106496K), [Metaspace: 4683K->4683K(1056768K)], 0.0071742 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Allocation Failure) [PSYoungGen: 15362K->15362K(18944K)] [ParOldGen: 87226K->87226K(87552K)] 102589K->102589K(106496K), [Metaspace: 4683K->4683K(1056768K)], 0.0028789 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 18944K, used 16002K [0x00000000fd580000, 0x0000000100000000, 0x0000000100000000)
eden space 16384K, 97% used [0x00000000fd580000,0x00000000fe520860,0x00000000fe580000)
from space 2560K, 0% used [0x00000000fe580000,0x00000000fe580000,0x00000000fe800000)
to space 14336K, 0% used [0x00000000ff200000,0x00000000ff200000,0x0000000100000000)
ParOldGen total 87552K, used 87226K [0x00000000f8000000, 0x00000000fd580000, 0x00000000fd580000)
object space 87552K, 99% used [0x00000000f8000000,0x00000000fd52ea90,0x00000000fd580000)
Metaspace used 4715K, capacity 4882K, committed 4992K, reserved 1056768K
class space used 523K, capacity 559K, committed 640K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at concurrent.lrucache.strongRefercence.Reference.
(Reference.java:11) at concurrent.lrucache.strongRefercence.Test.lambda$test2$1(Test.java:23)
at concurrent.lrucache.strongRefercence.Test$$Lambda$1/159413332.load(Unknown Source)
at concurrent.lrucache.strongRefercence.LRUCache.get(LRUCache.java:51)
at concurrent.lrucache.strongRefercence.Test.test2(Test.java:25)
at concurrent.lrucache.strongRefercence.Test.main(Test.java:8)
强引用产生了内存溢出问题。



