首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >调优GC以进行大容量缓存刷新

调优GC以进行大容量缓存刷新
EN

Stack Overflow用户
提问于 2022-03-24 13:02:44
回答 2查看 330关注 0票数 1

内存中有一个很大的缓存(使用com.google.common.cache.LoadingCache实现)。通过使用如下所示的Scheduler,在10分钟后就会刷新它:

代码语言:javascript
复制
ScheduledExecutorService refresher = Executors.newSingleThreadScheduledExecutor();
    refresher.scheduleWithFixedDelay(
            new Runnable() {
                public void run() {
                    for (String key : cache.asMap().keySet()) {
                        cache.refresh(key);
                    }
                }
            }, 0, 10, TimeUnit.MINUTES);

我面临的问题是:每隔10分钟,老一代的空间就会有一个巨大的增长。我假设正在发生这种情况,因为刷新正在发生,旧对象没有像创建新对象那样被快速清除。这可以在下面的图表中看到。(注意:这个图表显示了CMS GC,但我在G1GC中得到了相同的结果)。

我可以采取哪些步骤来优化这个特定的问题?我可以使用CMS或G1GC,但这些尖峰必须扁平。原因是,完整的GC正在被踢进来,而应用程序正在进入Stop The World GC

我能做些什么来优化缓存的代码,以便更有效地刷新它,这样就不会出现突然的尖峰?

EN

回答 2

Stack Overflow用户

发布于 2022-03-24 19:38:08

,我假设这是因为刷新正在发生,旧对象没有像创建新对象那样被快速清除。

使用分配分析器可以帮助检验这一假设。

,我可以采取哪些步骤来优化这个特定的问题?我可以使用CMS或G1GC,但这些尖峰必须扁平。原因是,完整的GC正在被踢进来,而应用程序将停止世界GC。

完整的GC通常表示收集器的并发部分无法跟上分配速率。但是要验证,您应该检查GC日志,以确定完整的GC原因。您可以尝试降低IHOP,为并发GC工作提供额外的CPU核心,或者调整刷新任务的节奏,以便它们在10分钟的间隔内更分散。

您还应该检查混合集合(假设为G1GC)的GC启发式日志,它是否能够为混合集合选择任何区域。如果它直接从年轻的集合转到一个完整的GC,而没有任何混合集合,那么其他一些启发式方法可能会因为缓存的分配模式而出现问题。

票数 1
EN

Stack Overflow用户

发布于 2022-03-29 08:31:26

GC调优可能会稍微改善症状,但更好地解决问题的根源。

智能装载机

首先,避免整个缓存内容被替换。您的应用程序要求刷新数据,并且不超过10分钟。但最有可能的是,实际上只有一小部分数据被修改了。如果数据未被修改,则保留现有对象。在您的reload中实现CacheLoader。我想它还没用呢。

reload的默认实现是:

代码语言:javascript
复制
  public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
    checkNotNull(key);
    checkNotNull(oldValue);
    return Futures.immediateFuture(load(key));
  }

确保equals对该值可用,并将其更改为:

代码语言:javascript
复制
  public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
    checkNotNull(key);
    checkNotNull(oldValue);
    V newValue = load(key);
    if (oldValue.equals(newValue)) {
      newValue = oldValue;
    }
    return Futures.immediateFuture(newValue);
  }

这仍然会产生大量的垃圾,但是保留了老一代,因此避免了整个GC。

如果数据源支持修改时间戳或散列,则有进一步改进的方法。下面是这个想法的草图:

代码语言:javascript
复制
  public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
    checkNotNull(key);
    checkNotNull(oldValue);
    V newValue = loadIfModifiedNullOtherwise(key, oldValue.stamp);
    if (newValue == null) {
      newValue = oldValue;
    }
    return Futures.immediateFuture(newValue);
  }

HTTP if-modified-since就是一个例子。如果缓存中的数据仍然是新的,则不会传输任何数据。

expireAfterWrite 使用缓存特性和 refreshAfterWrite

在一个固定的间隔刷新总是产生一个尖峰。使用缓存特性并使用以下方法构建缓存,而不是预定的刷新:

代码语言:javascript
复制
   builder
     .expireAfterWrite(10, TimeUnit.MINUTES)
     .refreshAfterWrite(5, TimeUnit.MINUTES);

这将具有以下优点:

  • 它有效地将刷新活动随机化,因为加载是由用户请求
  • 触发的,它将减少内存占用。在没有访问10分钟之后,缓存的项将被删除(
  • ),它很可能会减少对数据源

的负载请求数量。

它也有一个潜在的缺点。对于未频繁访问的条目,访问延迟越来越高,因为用户请求需要等待加载。在这个领域,缓存实现可以改进,但在大多数情况下,这些特性已经足够好了。

请注意,番石榴缓存已不再积极开发。考虑一下最近的一种选择。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71603019

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档