CaffeineCache 慎用weakKeys

栏目: Java · 发布时间: 5年前

内容简介:前两天在一个Spring项目里使用了Caffine缓存,在application.yml中的配置如下:为了避免缓存占用过多内存导致频繁GC,使用了weakKeys和weakValues选项。不过测试时发现缓存不能命中,仍然会查询数据库。

前两天在一个Spring项目里使用了Caffine缓存,在application.yml中的配置如下:

  cache:
    type: CAFFEINE
    caffeine:
      spec: initialCapacity=1048576,maximumSize=1073741824,weakKeys,weakValues,expireAfterAccess=10m

为了避免缓存占用过多内存导致频繁GC,使用了weakKeys和weakValues选项。

不过测试时发现缓存不能命中,仍然会查询数据库。

通过debug发现,caffine使用WeakKeyReference将缓存的key做了封装。WeakKeyReference的结构如下:

static class WeakKeyReference<K> 
    extends WeakReference<K> implements InternalReference<K> {
    
    private final int hashCode;
 
    public WeakKeyReference(@Nullable K key, @Nullable ReferenceQueue<K> queue) {
      super(key, queue);
      hashCode = System.identityHashCode(key);
    }
 
    @Override
    public Object getKeyReference() {
      return this;
    }
 
    @Override
    public boolean equals(Object object) {
      return referenceEquals(object);
    }
 
    @Override
    public int hashCode() {
      return hashCode;
    }
  }

需要注意这里的equals()和hashCode()方法。

equals()调用的referenceEquals()方法是接口InternalReference的default方法,具体为:

    default boolean referenceEquals(@Nullable Object object) {
      if (object == this) {
        return true;
      } else if (object instanceof InternalReference<?>) {
        InternalReference<?> referent = (InternalReference<?>) object;
        return (get() == referent.get());
      }
      return false;
    }

referenceEquals()方法中调用的get()方法在WeakKeyReference类中获取的是key的原始值。在方法中对两个key是否一致的判定使用的是 == ,而非是equals()。也就是说需要两个key指向同一个对象才能被认为是一致的。

hashCode()的实现也与equals()方法呼应。生成hashCode使用的是 System . identityHashCode ( ) 。identityHashCode方法是jre的一个native方法,这个方法的注释如下:

    /**
     * Returns the same hash code for the given object as
     * would be returned by the default method hashCode(),
     * whether or not the given object's class overrides
     * hashCode().
     * The hash code for the null reference is zero.
     */

注释说明这个方法对于指定的对象会返回相同的hashCode。即这个方法是针对对象进行操作的,比如两个字符串对象,即使其字符序列相同,通过identityHashCode方法生成的hashCode也不会相同。 看一个示例程序:

    public static void main(String[] args) throws IOException {
        System.out.println(System.identityHashCode(new String("zhyea")));
        System.out.println(System.identityHashCode(new String("zhyea")));
    }

示例程序输出了相通字符序列“zhyea”的两个字符串对象的identityHashCode执行结果,结果为:

可以看到最终结果是不同的。

到现在缓存不能命中的原因应该是找到了:因为使用了weakKeys选项,caffine使用WeakKeyReference封装了缓存key,导致相同字符序列的不同String对象的key被视为是不同的缓存主键。

果然在去掉weakKeys和weakValues配置项后,测试发现缓存能够命中了。

后来在 Caffeine的文档 中找到了如下说明:

Caffeine . weakKeys ( ) stores keys using weak references. This allows entries to be garbage-collected if there are no other strong references to the keys. Since garbage collection depends only on identity equality, this causes the whole cache to use identity (==) equality to compare keys, instead of equals ( ) .

文档中提到因为GC的限制,需要对weakKey使用“==”替换equals()。

原因算是找到了,不过回过头来想想,在Spring中Caffeine的weakKeys选项确实有些鸡肋:Spring的CacheKey生成方式导致weakKey必然指向不同的对象,结果就是缓存注定不能命中,并且每次调用都会在缓存中插入一条新的记录。这样尽管使用weakKey不会造成内存泄漏,可是也会增加GC负担。因此在SpringBoot中使用Caffeine时需要慎用weakKeys。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

一目了然

一目了然

Robert Hoekman, Jr. / 段江玲 / 机械工业出版社华章公司 / 2012-3-19 / 59.00元

可用性或易用性是软件或Web设计师的重要设计目标之一。本书深入阐述了如何设计出简单易用的基于Web的软件,以帮助读者理解、掌握显性设计的精髓。作者从软件开发初期谈起,一直到软件设计后期,分析诸多案例并论证了自己的见解或设计原则。本书在第1版的基础上进行了重大改进,尤其是在设计思想上,作者在本书中谈到“以用户为中心的设计”、“以任务为中心的设计”以及“以情景为中心的设计”的理念。这种设计理念也将更直......一起来看看 《一目了然》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具