内存中有一个很大的缓存(使用com.google.common.cache.LoadingCache实现)。通过使用如下所示的Scheduler,在10分钟后就会刷新它:
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。
我能做些什么来优化缓存的代码,以便更有效地刷新它,这样就不会出现突然的尖峰?
发布于 2022-03-24 19:38:08
,我假设这是因为刷新正在发生,旧对象没有像创建新对象那样被快速清除。
使用分配分析器可以帮助检验这一假设。
,我可以采取哪些步骤来优化这个特定的问题?我可以使用CMS或G1GC,但这些尖峰必须扁平。原因是,完整的GC正在被踢进来,而应用程序将停止世界GC。
完整的GC通常表示收集器的并发部分无法跟上分配速率。但是要验证,您应该检查GC日志,以确定完整的GC原因。您可以尝试降低IHOP,为并发GC工作提供额外的CPU核心,或者调整刷新任务的节奏,以便它们在10分钟的间隔内更分散。
您还应该检查混合集合(假设为G1GC)的GC启发式日志,它是否能够为混合集合选择任何区域。如果它直接从年轻的集合转到一个完整的GC,而没有任何混合集合,那么其他一些启发式方法可能会因为缓存的分配模式而出现问题。
发布于 2022-03-29 08:31:26
GC调优可能会稍微改善症状,但更好地解决问题的根源。
智能装载机
首先,避免整个缓存内容被替换。您的应用程序要求刷新数据,并且不超过10分钟。但最有可能的是,实际上只有一小部分数据被修改了。如果数据未被修改,则保留现有对象。在您的reload中实现CacheLoader。我想它还没用呢。
reload的默认实现是:
public ListenableFuture<V> reload(K key, V oldValue) throws Exception {
checkNotNull(key);
checkNotNull(oldValue);
return Futures.immediateFuture(load(key));
}确保equals对该值可用,并将其更改为:
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。
如果数据源支持修改时间戳或散列,则有进一步改进的方法。下面是这个想法的草图:
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
在一个固定的间隔刷新总是产生一个尖峰。使用缓存特性并使用以下方法构建缓存,而不是预定的刷新:
builder
.expireAfterWrite(10, TimeUnit.MINUTES)
.refreshAfterWrite(5, TimeUnit.MINUTES);这将具有以下优点:
的负载请求数量。
它也有一个潜在的缺点。对于未频繁访问的条目,访问延迟越来越高,因为用户请求需要等待加载。在这个领域,缓存实现可以改进,但在大多数情况下,这些特性已经足够好了。
请注意,番石榴缓存已不再积极开发。考虑一下最近的一种选择。
https://stackoverflow.com/questions/71603019
复制相似问题