package se.l4.vibe.probes;
import java.lang.management.BufferPoolMXBean;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import se.l4.vibe.VibeException;
import se.l4.vibe.mapping.KeyValueMappable;
import se.l4.vibe.mapping.KeyValueReceiver;
import se.l4.vibe.mapping.KeyValueToString;
/**
* Probes for the JVM runtime.
*
* @author Andreas Holstenson
*
*/
public class JvmProbes
{
private JvmProbes()
{
}
/**
* Get a probe for measuring the CPU usage of this JVM process as factor
* between 0 to 1.
*
* @return
*/
public static SampledProbe<Double> cpuUsage()
{
final com.sun.management.OperatingSystemMXBean os =
(com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
final Runtime rt = Runtime.getRuntime();
return new AbstractSampledProbe<Double>()
{
private long lastUptime = runtime.getUptime();
private long lastCpu = os.getProcessCpuTime();
@Override
public Double peek()
{
long uptime = runtime.getUptime();
long cpu = os.getProcessCpuTime();
if(lastUptime == 0)
{
lastUptime = uptime;
lastCpu = cpu;
return Double.NaN;
}
long elapsedCpu = cpu - lastCpu;
long elapsedTime = uptime - lastUptime;
// Calculate and return, do not store new values
return Math.min(99, elapsedCpu / (elapsedTime * 10000.0 * rt.availableProcessors())) / 100;
}
@Override
protected Double sample0()
{
long uptime = runtime.getUptime();
long cpu = os.getProcessCpuTime();
if(lastUptime == 0)
{
lastUptime = uptime;
lastCpu = cpu;
return Double.NaN;
}
long elapsedCpu = cpu - lastCpu;
long elapsedTime = uptime - lastUptime;
double v = Math.min(99, elapsedCpu / (elapsedTime * 10000.0 * rt.availableProcessors())) / 100;
lastUptime = uptime;
lastCpu = cpu;
return v;
}
};
}
public static SampledProbe<MemoryDetails> memoryUsage()
{
final MemoryMXBean b = ManagementFactory.getMemoryMXBean();
return new AbstractSampledProbe<MemoryDetails>()
{
@Override
public MemoryDetails sample0()
{
MemoryUsage heap = b.getHeapMemoryUsage();
MemoryUsage nonHeap = b.getNonHeapMemoryUsage();
return new MemoryDetails(
heap.getUsed(), heap.getCommitted(), heap.getMax(),
nonHeap.getUsed(), nonHeap.getCommitted(), nonHeap.getMax()
);
}
@Override
public MemoryDetails peek()
{
return sample0();
}
};
}
/**
* Get a probe for monitoring heap memory usage within the JVM.
*
* @return
*/
public static SampledProbe<Long> heapMemoryUsage()
{
final MemoryMXBean b = ManagementFactory.getMemoryMXBean();
return new AbstractSampledProbe<Long>()
{
@Override
public Long sample0()
{
return b.getHeapMemoryUsage().getUsed();
}
@Override
public Long peek()
{
return sample0();
}
};
}
/**
* Get a probe for monitoring non heap memory usage within the JVM.
*
* @return
*/
public static SampledProbe<Long> nonHeapMemoryUsage()
{
final MemoryMXBean b = ManagementFactory.getMemoryMXBean();
return new AbstractSampledProbe<Long>()
{
@Override
public Long sample0()
{
return b.getNonHeapMemoryUsage().getUsed();
}
@Override
public Long peek()
{
return sample0();
}
};
}
/**
* Get a probe for memory usage within the JVM.
*
* @return
*/
public static SampledProbe<Long> totalUsedMemory()
{
final MemoryMXBean b = ManagementFactory.getMemoryMXBean();
return new AbstractSampledProbe<Long>()
{
@Override
public Long sample0()
{
return b.getHeapMemoryUsage().getUsed() + b.getNonHeapMemoryUsage().getUsed();
}
@Override
public Long peek()
{
return sample0();
}
};
}
/**
* Get a probe for heap memory usage within the JVM.
*
* @return
*/
public static SampledProbe<Double> heapMemoryAsFraction()
{
final MemoryMXBean b = ManagementFactory.getMemoryMXBean();
return new AbstractSampledProbe<Double>()
{
@Override
public Double sample0()
{
MemoryUsage heap = b.getHeapMemoryUsage();
return heap.getUsed() / (double) heap.getMax();
}
@Override
public Double peek()
{
return sample0();
}
};
}
/**
* Get details about the direct buffers allocated by the JVM.
*
* @return
*/
public static SampledProbe<BufferPoolDetails> directBufferPool()
{
return bufferPool("direct");
}
/**
* Get details about the mapped buffers allocated by the JVM.
*
* @return
*/
public static SampledProbe<BufferPoolDetails> mappedBufferPool()
{
return bufferPool("mapped");
}
private static SampledProbe<BufferPoolDetails> bufferPool(String name)
{
for(BufferPoolMXBean b : ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class))
{
if(b.getName().equals(name))
{
return new AbstractSampledProbe<BufferPoolDetails>()
{
@Override
public BufferPoolDetails peek()
{
return sample0();
}
@Override
protected BufferPoolDetails sample0()
{
return new BufferPoolDetails(b.getMemoryUsed(), b.getTotalCapacity(), b.getCount());
}
};
}
}
throw new VibeException("Unknown buffer pool: " + name);
}
/**
* Probe that measures the uptime of the JVM in milliseconds.
*
* @return
*/
public static Probe<Long> uptime()
{
final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
return new Probe<Long>()
{
@Override
public Long read()
{
return runtime.getUptime();
}
};
}
/**
* Get a probe suitable for sampling the number of threads active.
*
* @return
*/
public static SampledProbe<Integer> threadCount()
{
final ThreadMXBean thread = ManagementFactory.getThreadMXBean();
return new AbstractSampledProbe<Integer>()
{
@Override
public Integer peek()
{
return sample0();
}
@Override
protected Integer sample0()
{
return thread.getThreadCount();
}
};
}
/**
* Get the number of classes that are currently loaded by the JVM.
*
* @return
*/
public static SampledProbe<Integer> loadedClassCount()
{
final ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean();
return new AbstractSampledProbe<Integer>()
{
@Override
public Integer peek()
{
return sample0();
}
@Override
protected Integer sample0()
{
return cl.getLoadedClassCount();
}
};
}
/**
* Get the number of open file descriptors.
*
* @return
*/
public static SampledProbe<Long> openFileDescriptorCount()
{
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
if(os instanceof com.sun.management.UnixOperatingSystemMXBean)
{
com.sun.management.UnixOperatingSystemMXBean unix =
(com.sun.management.UnixOperatingSystemMXBean) os;
return new AbstractSampledProbe<Long>()
{
@Override
public Long peek()
{
return sample0();
}
@Override
protected Long sample0()
{
return unix.getOpenFileDescriptorCount();
}
};
}
return new ConstantProbe<Long>(-1l);
}
public static class MemoryDetails
implements KeyValueMappable
{
private final long heapUsed;
private final long heapCommitted;
private final long heapMax;
private final long nonHeapUsed;
private final long nonHeapCommitted;
private final long nonHeapMax;
public MemoryDetails(long heapUsed, long heapCommitted, long heapMax,
long nonHeapUsed, long nonHeapCommitted, long nonHeapMax)
{
this.heapUsed = heapUsed;
this.heapCommitted = heapCommitted;
this.heapMax = heapMax;
this.nonHeapUsed = nonHeapUsed;
this.nonHeapCommitted = nonHeapCommitted;
this.nonHeapMax = nonHeapMax;
}
public long getHeapUsed()
{
return heapUsed;
}
public long getHeapCommitted()
{
return heapCommitted;
}
public long getHeapMax()
{
return heapMax;
}
public long getNonHeapUsed()
{
return nonHeapUsed;
}
public long getNonHeapCommitted()
{
return nonHeapCommitted;
}
public long getNonHeapMax()
{
return nonHeapMax;
}
public double getHeapUsageAsFraction()
{
return heapUsed / (double) heapMax;
}
@Override
public void mapToKeyValues(KeyValueReceiver receiver)
{
receiver.add("heapUsed", heapUsed);
receiver.add("heapCommitted", heapCommitted);
receiver.add("heapMax", heapMax);
receiver.add("nonHeapUsed", nonHeapUsed);
receiver.add("nonHeapCommitted", nonHeapCommitted);
receiver.add("nonHeapMax", nonHeapMax);
receiver.add("heapUsageAsFraction", getHeapUsageAsFraction());
}
@Override
public String toString()
{
return KeyValueToString.toString(this);
}
}
public static class BufferPoolDetails
implements KeyValueMappable
{
private final long memoryUsed;
private final long totalCapacity;
private final long count;
public BufferPoolDetails(long memoryUsed, long totalCapacity, long count)
{
this.memoryUsed = memoryUsed;
this.totalCapacity = totalCapacity;
this.count = count;
}
public long getMemoryUsed()
{
return memoryUsed;
}
public long getTotalCapacity()
{
return totalCapacity;
}
public long getCount()
{
return count;
}
@Override
public void mapToKeyValues(KeyValueReceiver receiver)
{
receiver.add("memoryUsed", memoryUsed);
receiver.add("totalCapacity", totalCapacity);
receiver.add("count", count);
}
@Override
public String toString()
{
return KeyValueToString.toString(this);
}
}
}