/*
* Copyright 2016 the original author or authors.
*
* 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 org.gradle.launcher.daemon.server.health.gc;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.gradle.api.Transformer;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.specs.Spec;
import org.gradle.util.CollectionUtils;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class GarbageCollectionMonitor {
public static final int POLL_INTERVAL_SECONDS = 1;
private static final int POLL_DELAY_SECONDS = 1;
private static final int EVENT_WINDOW = 20;
private static final Logger LOGGER = Logging.getLogger(GarbageCollectionMonitor.class);
private final Map<String, SlidingWindow<GarbageCollectionEvent>> events;
private final GarbageCollectorMonitoringStrategy gcStrategy;
private final ScheduledExecutorService pollingExecutor;
public GarbageCollectionMonitor(ScheduledExecutorService pollingExecutor) {
this(determineGcStrategy(), pollingExecutor);
}
public GarbageCollectionMonitor(GarbageCollectorMonitoringStrategy gcStrategy, ScheduledExecutorService pollingExecutor) {
this.pollingExecutor = pollingExecutor;
this.gcStrategy = gcStrategy;
if (gcStrategy != GarbageCollectorMonitoringStrategy.UNKNOWN) {
events = ImmutableMap.<String, SlidingWindow<GarbageCollectionEvent>>of(
gcStrategy.getTenuredPoolName(), new DefaultSlidingWindow<GarbageCollectionEvent>(EVENT_WINDOW),
gcStrategy.getPermGenPoolName(), new DefaultSlidingWindow<GarbageCollectionEvent>(EVENT_WINDOW)
);
pollForValues(gcStrategy.getGarbageCollectorName(), ImmutableList.copyOf(events.keySet()));
} else {
events = ImmutableMap.<String, SlidingWindow<GarbageCollectionEvent>>builder().build();
}
}
private static GarbageCollectorMonitoringStrategy determineGcStrategy() {
JVMStrategy jvmStrategy = JVMStrategy.current();
final List<String> garbageCollectors = CollectionUtils.collect(ManagementFactory.getGarbageCollectorMXBeans(), new Transformer<String, GarbageCollectorMXBean>() {
@Override
public String transform(GarbageCollectorMXBean garbageCollectorMXBean) {
return garbageCollectorMXBean.getName();
}
});
GarbageCollectorMonitoringStrategy gcStrategy = CollectionUtils.findFirst(jvmStrategy.getGcStrategies(), new Spec<GarbageCollectorMonitoringStrategy>() {
@Override
public boolean isSatisfiedBy(GarbageCollectorMonitoringStrategy strategy) {
return garbageCollectors.contains(strategy.getGarbageCollectorName());
}
});
if (gcStrategy == null) {
LOGGER.info("Unable to determine a garbage collection monitoring strategy for " + jvmStrategy.toString());
return GarbageCollectorMonitoringStrategy.UNKNOWN;
} else {
return gcStrategy;
}
}
private void pollForValues(String garbageCollectorName, List<String> memoryPoolNames) {
pollingExecutor.scheduleAtFixedRate(new GarbageCollectionCheck(events, memoryPoolNames, garbageCollectorName), POLL_DELAY_SECONDS, POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
public GarbageCollectionStats getTenuredStats() {
return getGarbageCollectionStatsWithEmptyDefault(gcStrategy.getTenuredPoolName());
}
public GarbageCollectionStats getPermGenStats() {
return getGarbageCollectionStatsWithEmptyDefault(gcStrategy.getPermGenPoolName());
}
private GarbageCollectionStats getGarbageCollectionStatsWithEmptyDefault(final String memoryPoolName) {
SlidingWindow<GarbageCollectionEvent> slidingWindow;
if ((memoryPoolName == null) || events.get(memoryPoolName) == null) { // events has no entries on UNKNOWN
slidingWindow = new DefaultSlidingWindow<GarbageCollectionEvent>(EVENT_WINDOW);
} else {
slidingWindow = events.get(memoryPoolName);
}
return new GarbageCollectionStats(slidingWindow.snapshot());
}
public GarbageCollectorMonitoringStrategy getGcStrategy() {
return gcStrategy;
}
public enum JVMStrategy {
IBM(GarbageCollectorMonitoringStrategy.IBM_ALL),
ORACLE_HOTSPOT(GarbageCollectorMonitoringStrategy.ORACLE_PARALLEL_CMS,
GarbageCollectorMonitoringStrategy.ORACLE_SERIAL,
GarbageCollectorMonitoringStrategy.ORACLE_6_CMS,
GarbageCollectorMonitoringStrategy.ORACLE_G1),
UNSUPPORTED();
final GarbageCollectorMonitoringStrategy[] gcStrategies;
JVMStrategy(GarbageCollectorMonitoringStrategy... gcStrategies) {
this.gcStrategies = gcStrategies;
}
static JVMStrategy current() {
String vmname = System.getProperty("java.vm.name");
if (vmname.equals("IBM J9 VM")) {
return IBM;
}
if (vmname.startsWith("Java HotSpot(TM)")) {
return ORACLE_HOTSPOT;
}
return UNSUPPORTED;
}
public GarbageCollectorMonitoringStrategy[] getGcStrategies() {
return gcStrategies;
}
@Override
public String toString() {
return "JVMStrategy{" + System.getProperty("java.vendor") + " version " + System.getProperty("java.version") + "}";
}
}
}