/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.presto.server;
import io.airlift.log.Logger;
import io.airlift.units.Duration;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
final class CodeCacheGcTrigger
{
private static final Logger log = Logger.get(CodeCacheGcTrigger.class);
private static final AtomicBoolean installed = new AtomicBoolean();
private final Duration interval;
private final int collectionThreshold;
@Inject
public CodeCacheGcTrigger(CodeCacheGcConfig config)
{
this.interval = config.getCodeCacheCheckInterval();
this.collectionThreshold = config.getCodeCacheCollectionThreshold();
}
@PostConstruct
public void start()
{
installCodeCacheGcTrigger();
}
public void installCodeCacheGcTrigger()
{
if (installed.getAndSet(true)) {
return;
}
// Hack to work around bugs in java 8 (8u45+) related to code cache management.
// See http://openjdk.5641.n7.nabble.com/JIT-stops-compiling-after-a-while-java-8u45-td259603.html for more info.
MemoryPoolMXBean codeCacheMbean = findCodeCacheMBean();
Thread gcThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
long used = codeCacheMbean.getUsage().getUsed();
long max = codeCacheMbean.getUsage().getMax();
if (used > 0.95 * max) {
log.error("Code Cache is more than 95% full. JIT may stop working.");
}
if (used > (max * collectionThreshold) / 100) {
// Due to some obscure bug in hotspot (java 8), once the code cache fills up the JIT stops compiling
// By forcing a GC, we let the code cache evictor make room before the cache fills up.
log.info("Triggering GC to avoid Code Cache eviction bugs");
System.gc();
}
try {
TimeUnit.MILLISECONDS.sleep(interval.toMillis());
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
gcThread.setDaemon(true);
gcThread.setName("Code-Cache-GC-Trigger");
gcThread.start();
}
private static MemoryPoolMXBean findCodeCacheMBean()
{
for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) {
if (bean.getName().equals("Code Cache")) {
return bean;
}
}
throw new RuntimeException("Could not obtain a reference to the 'Code Cache' MemoryPoolMXBean");
}
}