guava 缓存过期策略

本文探讨了Google Guava库中缓存的过期策略,指出其主要通过惰性移除来处理过期,即在get时判断是否过期。如果长时间不访问可能导致内存泄漏。Guava的LocalCache使用类似ConcurrentHashMap的数据结构,通过Segment的getLiveEntry方法处理存活的条目。当达到最大容量时,Guava使用LRU算法移除旧缓存,该算法基于队列进行排序。Guava的实现为本地缓存需求提供了一个参考方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

google的guava可谓鼎鼎有名,最近在研究缓存,也就看看它是怎么处理缓存过期问题的;首先它并没有通过在后台起一个线程,不停去轮询。不这么做主要是为了效率吧,也就是所谓的惰性移除,在get时判断是否过期。那如果一直不访问,可能存在内存泄漏问题。

示例代码:

 Cache<Object, Object> cache = CacheBuilder.newBuilder().expireAfterAccess(2,TimeUnit.SECONDS).build();
        cache.put("a",1);
        System.out.println(cache.getIfPresent("a"));

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(cache.getIfPresent("a"));

1,CacheBuilder默认的缓存实现为LocalCache,所以这里我们主要去研究LocalCache的getIfPresent 即可

2,通过观察,我们可以猜出LocalCache 是用类似于ConcurrentHashMap 的数据结构来保存数据的

3,这里我们主要看其Segment 的get 方法,然后进入getLiveEntry 方法,看名字感觉跟存活有关,点进去

    ReferenceEntry<K, V> getLiveEntry(Object key, int hash, long now) {
//获取值
      ReferenceEntry<K, V> e = getEntry(key, hash);
      if (e == null) {//如果为空,返回空
        return null;
      } else if (map.isExpired(e, now)) {//判断是否过期
        tryExpireEntries(now);
        return null;
      }
      return e;
    }


boolean isExpired(ReferenceEntry<K, V> entry, long now) {
    checkNotNull(entry);
    if (expiresAfterAccess() && (now - entry.getAccessTime() >= expireAfterAccessNanos)) {
      return true;
    }
    if (expiresAfterWrite() && (now - entry.getWriteTime() >= expireAfterWriteNanos)) {
      return true;
    }
    return false;
  }

maximumSize作用原理,猜想:应该是在put缓存时,检查是否达到了最大值,如果达到则用LRU算法移除一个cache

1,观察LocalCache#put

 V put(K key, int hash, V value, boolean onlyIfAbsent) {
      lock();
      try {
       ...
        evictEntries(newEntry);//这行代码,看名字感觉就是它,点进去看
        return null;
      } finally {
        unlock();
        postWriteCleanup();
      }
    }

void evictEntries(ReferenceEntry<K, V> newest) {
     ....

      while (totalWeight > maxSegmentWeight) {
        ReferenceEntry<K, V> e = getNextEvictable();
        if (!removeEntry(e, e.getHash(), RemovalCause.SIZE)) {
          throw new AssertionError();
        }
      }
    }

这里我们不妨看看它的LRU算法是如何实现的

    ReferenceEntry<K, V> getNextEvictable() {
      for (ReferenceEntry<K, V> e : accessQueue) {
        int weight = e.getValueReference().getWeight();
        if (weight > 0) {
          return e;
        }
      }
      throw new AssertionError();
    }

发现它是用队列实现的,也就是在插入新缓存是有排序。

总结:

我们时常会有本地缓存的需求,这时不妨看看google是怎么做的,可以给我们一个参考

转载于:https://my.oschina.net/u/3574106/blog/3016413

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值