package org.radargun.sysmonitor;
import static java.lang.management.ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.io.Serializable;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import org.radargun.reporting.Timeline;
import org.radargun.traits.JmxConnectionProvider;
/**
* In each invocation of the {@link #run()} method, retrieves information
* about garbage collection from JMX and reports it into the {@link Timeline}.
*
* @author Galder Zamarreno
*/
public class GcMonitor extends JmxMonitor implements Serializable {
private static final String GC_USAGE = "GC CPU usage";
private long prevGcTime;
private long prevUpTime;
public GcMonitor(JmxConnectionProvider jmxConnectionProvider, Timeline timeline) {
super(jmxConnectionProvider, timeline);
}
public synchronized void run() {
try {
if (connection == null) {
log.warn("MBean connection is not open, cannot read GC stats");
return;
}
OperatingSystemMXBean os = ManagementFactory.newPlatformMXBeanProxy(connection,
ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, OperatingSystemMXBean.class);
int procCount = os.getAvailableProcessors();
List<GarbageCollectorMXBean> gcMbeans = getGarbageCollectorMXBeans(connection);
long gcTime = 0;
for (GarbageCollectorMXBean gcBean : gcMbeans)
gcTime += gcBean.getCollectionTime();
long processGcTimeDiff = TimeUnit.MILLISECONDS.toNanos(gcTime - prevGcTime) / procCount;
long upTime = (Long) connection.getAttribute(RUNTIME_NAME, PROCESS_UP_TIME);
long upTimeDiff = TimeUnit.MILLISECONDS.toNanos(upTime - prevUpTime);
double gcUsage = Math.min(1d, Math.max(0, (double) processGcTimeDiff / (double) upTimeDiff));
timeline.addValue(GC_USAGE, new Timeline.Value(gcUsage));
log.tracef("Current GC CPU usage: %.2f%%", 100 * gcUsage);
prevUpTime = upTime;
prevGcTime = gcTime;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
private List<GarbageCollectorMXBean> getGarbageCollectorMXBeans(MBeanServerConnection con) throws Exception {
List<GarbageCollectorMXBean> gcMbeans = null;
// TODO: List changes, so can't really cache apparently, but how performant is this?
if (con != null) {
ObjectName gcName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
Set<ObjectName> mbeans = con.queryNames(gcName, null);
if (mbeans != null) {
gcMbeans = new ArrayList<GarbageCollectorMXBean>();
for (ObjectName on : mbeans) {
String name = GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",name=" + on.getKeyProperty("name");
GarbageCollectorMXBean mbean = newPlatformMXBeanProxy(con, name, GarbageCollectorMXBean.class);
gcMbeans.add(mbean);
}
}
}
return gcMbeans;
}
@Override
public synchronized void start() {
super.start();
timeline.addValue(GC_USAGE, new Timeline.Value(0));
}
@Override
public synchronized void stop() {
super.stop();
timeline.addValue(GC_USAGE, new Timeline.Value(0));
}
}