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。


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

查看所有标签

猜你喜欢:

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

水平营销

水平营销

[美] 菲利普·科特勒、费尔南多・德・巴斯 / 陈燕茹 / 中信出版社 / 2005-1 / 25.00元

《水平营销》阐明了相对纵向营销而言的的水平营销的框架和理论。引入横向思维来作为发现新的营销创意的又一平台,旨在获得消费者不可能向营销研究人员要求或建议的点子。而这些点子将帮助企业在产品愈加同质和超竞争的市场中立于不败之地。 《水平营销》提到: 是什么创新过程导致加油站里开起了超市? 是什么创新过程导致取代外卖比萨服务的冷冻比萨的亮相? 是什么创新过程导致巧克力糖里冒出了玩具......一起来看看 《水平营销》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具