/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
/***************************************************************************************
* Attribution Notice
*
* This file is imported from Metrics (https://github.com/codahale/metrics subproject metrics-core).
* Metrics is Copyright (c) 2010-2012 Coda Hale, Yammer.com
* Metrics is Published under Apache Software License 2.0, see LICENSE in root folder.
*
* Thank you for the Metrics developers efforts in making their library available under an Apache license.
* EsperTech incorporates Metrics version 0.2.2 in source code form since Metrics depends on SLF4J
* and this dependency is not possible to introduce for Esper.
* *************************************************************************************
*/
package com.espertech.esper.metrics.codahale_metrics.metrics.core;
import javax.management.*;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.Thread.State;
import java.lang.management.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* A collection of Java Virtual Machine metrics.
*/
public class VirtualMachineMetrics {
private static final int MAX_STACK_TRACE_DEPTH = 100;
private static final VirtualMachineMetrics INSTANCE = new VirtualMachineMetrics(
ManagementFactory.getMemoryMXBean(),
ManagementFactory.getMemoryPoolMXBeans(),
ManagementFactory.getOperatingSystemMXBean(),
ManagementFactory.getThreadMXBean(),
ManagementFactory.getGarbageCollectorMXBeans(),
ManagementFactory.getRuntimeMXBean(),
ManagementFactory.getPlatformMBeanServer());
/**
* The default instance of {@link VirtualMachineMetrics}.
*
* @return the default {@link VirtualMachineMetrics instance}
*/
public static VirtualMachineMetrics getInstance() {
return INSTANCE;
}
/**
* Per-GC statistics.
*/
public static class GarbageCollectorStats {
private final long runs, timeMS;
private GarbageCollectorStats(long runs, long timeMS) {
this.runs = runs;
this.timeMS = timeMS;
}
/**
* Returns the number of times the garbage collector has run.
*
* @return the number of times the garbage collector has run
*/
public long getRuns() {
return runs;
}
/**
* Returns the amount of time in the given unit the garbage collector has taken in total.
*
* @param unit the time unit for the return value
* @return the amount of time in the given unit the garbage collector
*/
public long getTime(TimeUnit unit) {
return unit.convert(timeMS, TimeUnit.MILLISECONDS);
}
}
/**
* The management interface for a buffer pool, for example a pool of {@link
* java.nio.ByteBuffer#allocateDirect direct} or {@link java.nio.MappedByteBuffer mapped}
* buffers.
*/
public static class BufferPoolStats {
private final long count, memoryUsed, totalCapacity;
private BufferPoolStats(long count, long memoryUsed, long totalCapacity) {
this.count = count;
this.memoryUsed = memoryUsed;
this.totalCapacity = totalCapacity;
}
/**
* Returns an estimate of the number of buffers in the pool.
*
* @return An estimate of the number of buffers in this pool
*/
public long getCount() {
return count;
}
/**
* Returns an estimate of the memory that the Java virtual machine is using for this buffer
* pool. The value returned by this method may differ from the estimate of the total {@link
* #getTotalCapacity capacity} of the buffers in this pool. This difference is explained by
* alignment, memory allocator, and other implementation specific reasons.
*
* @return An estimate of the memory that the Java virtual machine is using for this buffer
* pool in bytes, or {@code -1L} if an estimate of the memory usage is not
* available
*/
public long getMemoryUsed() {
return memoryUsed;
}
/**
* Returns an estimate of the total capacity of the buffers in this pool. A buffer's
* capacity is the number of elements it contains and the value returned by this method is
* an estimate of the total capacity of buffers in the pool in bytes.
*
* @return An estimate of the total capacity of the buffers in this pool in bytes
*/
public long getTotalCapacity() {
return totalCapacity;
}
}
private final MemoryMXBean memory;
private final List<MemoryPoolMXBean> memoryPools;
private final OperatingSystemMXBean os;
private final ThreadMXBean threads;
private final List<GarbageCollectorMXBean> garbageCollectors;
private final RuntimeMXBean runtime;
private final MBeanServer mBeanServer;
VirtualMachineMetrics(MemoryMXBean memory,
List<MemoryPoolMXBean> memoryPools,
OperatingSystemMXBean os,
ThreadMXBean threads,
List<GarbageCollectorMXBean> garbageCollectors,
RuntimeMXBean runtime, MBeanServer mBeanServer) {
this.memory = memory;
this.memoryPools = memoryPools;
this.os = os;
this.threads = threads;
this.garbageCollectors = garbageCollectors;
this.runtime = runtime;
this.mBeanServer = mBeanServer;
}
/**
* Returns the total initial memory of the current JVM.
*
* @return total Heap and non-heap initial JVM memory in bytes.
*/
public double totalInit() {
return memory.getHeapMemoryUsage().getInit() +
memory.getNonHeapMemoryUsage().getInit();
}
/**
* Returns the total memory currently used by the current JVM.
*
* @return total Heap and non-heap memory currently used by JVM in bytes.
*/
public double totalUsed() {
return memory.getHeapMemoryUsage().getUsed() +
memory.getNonHeapMemoryUsage().getUsed();
}
/**
* Returns the total memory currently used by the current JVM.
*
* @return total Heap and non-heap memory currently used by JVM in bytes.
*/
public double totalMax() {
return memory.getHeapMemoryUsage().getMax() +
memory.getNonHeapMemoryUsage().getMax();
}
/**
* Returns the total memory committed to the JVM.
*
* @return total Heap and non-heap memory currently committed to the JVM in bytes.
*/
public double totalCommitted() {
return memory.getHeapMemoryUsage().getCommitted() +
memory.getNonHeapMemoryUsage().getCommitted();
}
/**
* Returns the heap initial memory of the current JVM.
*
* @return Heap initial JVM memory in bytes.
*/
public double heapInit() {
return memory.getHeapMemoryUsage().getInit();
}
/**
* Returns the heap memory currently used by the current JVM.
*
* @return Heap memory currently used by JVM in bytes.
*/
public double heapUsed() {
return memory.getHeapMemoryUsage().getUsed();
}
/**
* Returns the heap memory currently used by the current JVM.
*
* @return Heap memory currently used by JVM in bytes.
*/
public double heapMax() {
return memory.getHeapMemoryUsage().getMax();
}
/**
* Returns the heap memory committed to the JVM.
*
* @return Heap memory currently committed to the JVM in bytes.
*/
public double heapCommitted() {
return memory.getHeapMemoryUsage().getCommitted();
}
/**
* Returns the percentage of the JVM's heap which is being used.
*
* @return the percentage of the JVM's heap which is being used
*/
public double heapUsage() {
final MemoryUsage usage = memory.getHeapMemoryUsage();
return usage.getUsed() / (double) usage.getMax();
}
/**
* Returns the percentage of the JVM's non-heap memory (e.g., direct buffers) which is being
* used.
*
* @return the percentage of the JVM's non-heap memory which is being used
*/
public double nonHeapUsage() {
final MemoryUsage usage = memory.getNonHeapMemoryUsage();
return usage.getUsed() / (double) usage.getMax();
}
/**
* Returns a map of memory pool names to the percentage of that pool which is being used.
*
* @return a map of memory pool names to the percentage of that pool which is being used
*/
public Map<String, Double> memoryPoolUsage() {
final Map<String, Double> pools = new TreeMap<String, Double>();
for (MemoryPoolMXBean pool : memoryPools) {
final double max = pool.getUsage().getMax() == -1 ?
pool.getUsage().getCommitted() :
pool.getUsage().getMax();
pools.put(pool.getName(), pool.getUsage().getUsed() / max);
}
return Collections.unmodifiableMap(pools);
}
/**
* Returns the percentage of available file descriptors which are currently in use.
*
* @return the percentage of available file descriptors which are currently in use, or {@code
* NaN} if the running JVM does not have access to this information
*/
public double fileDescriptorUsage() {
try {
final Method getOpenFileDescriptorCount = os.getClass().getDeclaredMethod("getOpenFileDescriptorCount");
getOpenFileDescriptorCount.setAccessible(true);
final Long openFds = (Long) getOpenFileDescriptorCount.invoke(os);
final Method getMaxFileDescriptorCount = os.getClass().getDeclaredMethod("getMaxFileDescriptorCount");
getMaxFileDescriptorCount.setAccessible(true);
final Long maxFds = (Long) getMaxFileDescriptorCount.invoke(os);
return openFds.doubleValue() / maxFds.doubleValue();
} catch (NoSuchMethodException e) {
return Double.NaN;
} catch (IllegalAccessException e) {
return Double.NaN;
} catch (InvocationTargetException e) {
return Double.NaN;
}
}
/**
* Returns the version of the currently-running jvm.
*
* @return the version of the currently-running jvm, eg "1.6.0_24"
* @see <a href="http://java.sun.com/j2se/versioning_naming.html">J2SE SDK/JRE Version String
* Naming Convention</a>
*/
public String version() {
return System.getProperty("java.runtime.version");
}
/**
* Returns the name of the currently-running jvm.
*
* @return the name of the currently-running jvm, eg "Java HotSpot(TM) Client VM"
* @see <a href="http://download.oracle.com/javase/6/docs/api/java/lang/System.html#getProperties()">System.getProperties()</a>
*/
public String name() {
return System.getProperty("java.vm.name");
}
/**
* Returns the number of seconds the JVM process has been running.
*
* @return the number of seconds the JVM process has been running
*/
public long uptime() {
return TimeUnit.MILLISECONDS.toSeconds(runtime.getUptime());
}
/**
* Returns the number of live threads (includes {@link #daemonThreadCount()}.
*
* @return the number of live threads
*/
public int threadCount() {
return threads.getThreadCount();
}
/**
* Returns the number of live daemon threads.
*
* @return the number of live daemon threads
*/
public int daemonThreadCount() {
return threads.getDaemonThreadCount();
}
/**
* Returns a map of garbage collector names to garbage collector information.
*
* @return a map of garbage collector names to garbage collector information
*/
public Map<String, GarbageCollectorStats> garbageCollectors() {
final Map<String, GarbageCollectorStats> stats = new HashMap<String, GarbageCollectorStats>();
for (GarbageCollectorMXBean gc : garbageCollectors) {
stats.put(gc.getName(),
new GarbageCollectorStats(gc.getCollectionCount(),
gc.getCollectionTime()));
}
return Collections.unmodifiableMap(stats);
}
/**
* Returns a set of strings describing deadlocked threads, if any are deadlocked.
*
* @return a set of any deadlocked threads
*/
public Set<String> deadlockedThreads() {
final long[] threadIds = threads.findDeadlockedThreads();
if (threadIds != null) {
final Set<String> threads = new HashSet<String>();
for (ThreadInfo info : this.threads.getThreadInfo(threadIds, MAX_STACK_TRACE_DEPTH)) {
final StringBuilder stackTrace = new StringBuilder();
for (StackTraceElement element : info.getStackTrace()) {
stackTrace.append("\t at ").append(element.toString()).append('\n');
}
threads.add(
String.format(
"%s locked on %s (owned by %s):\n%s",
info.getThreadName(), info.getLockName(),
info.getLockOwnerName(),
stackTrace.toString()
)
);
}
return Collections.unmodifiableSet(threads);
}
return Collections.emptySet();
}
/**
* Returns a map of thread states to the percentage of all threads which are in that state.
*
* @return a map of thread states to percentages
*/
public Map<State, Double> threadStatePercentages() {
final Map<State, Double> conditions = new HashMap<State, Double>();
for (State state : State.values()) {
conditions.put(state, 0.0);
}
final long[] allThreadIds = threads.getAllThreadIds();
final ThreadInfo[] allThreads = threads.getThreadInfo(allThreadIds);
int liveCount = 0;
for (ThreadInfo info : allThreads) {
if (info != null) {
final State state = info.getThreadState();
conditions.put(state, conditions.get(state) + 1);
liveCount++;
}
}
for (State state : new ArrayList<State>(conditions.keySet())) {
conditions.put(state, conditions.get(state) / liveCount);
}
return Collections.unmodifiableMap(conditions);
}
/**
* Dumps all of the threads' current information to an output stream.
*
* @param out an output stream
*/
public void threadDump(OutputStream out) {
final ThreadInfo[] threads = this.threads.dumpAllThreads(true, true);
final PrintWriter writer = new PrintWriter(out, true);
for (int ti = threads.length - 1; ti >= 0; ti--) {
final ThreadInfo t = threads[ti];
writer.printf("%s id=%d state=%s",
t.getThreadName(),
t.getThreadId(),
t.getThreadState());
final LockInfo lock = t.getLockInfo();
if (lock != null && t.getThreadState() != State.BLOCKED) {
writer.printf("\n - waiting on <0x%08x> (a %s)",
lock.getIdentityHashCode(),
lock.getClassName());
writer.printf("\n - locked <0x%08x> (a %s)",
lock.getIdentityHashCode(),
lock.getClassName());
} else if (lock != null && t.getThreadState() == State.BLOCKED) {
writer.printf("\n - waiting to lock <0x%08x> (a %s)",
lock.getIdentityHashCode(),
lock.getClassName());
}
if (t.isSuspended()) {
writer.print(" (suspended)");
}
if (t.isInNative()) {
writer.print(" (running in native)");
}
writer.println();
if (t.getLockOwnerName() != null) {
writer.printf(" owned by %s id=%d\n", t.getLockOwnerName(), t.getLockOwnerId());
}
final StackTraceElement[] elements = t.getStackTrace();
final MonitorInfo[] monitors = t.getLockedMonitors();
for (int i = 0; i < elements.length; i++) {
final StackTraceElement element = elements[i];
writer.printf(" at %s\n", element);
for (int j = 1; j < monitors.length; j++) {
final MonitorInfo monitor = monitors[j];
if (monitor.getLockedStackDepth() == i) {
writer.printf(" - locked %s\n", monitor);
}
}
}
writer.println();
final LockInfo[] locks = t.getLockedSynchronizers();
if (locks.length > 0) {
writer.printf(" Locked synchronizers: count = %d\n", locks.length);
for (LockInfo l : locks) {
writer.printf(" - %s\n", l);
}
writer.println();
}
}
writer.println();
writer.flush();
}
public Map<String, BufferPoolStats> getBufferPoolStats() {
try {
final String[] attributes = {"Count", "MemoryUsed", "TotalCapacity"};
final ObjectName direct = new ObjectName("java.nio:type=BufferPool,name=direct");
final ObjectName mapped = new ObjectName("java.nio:type=BufferPool,name=mapped");
final AttributeList directAttributes = mBeanServer.getAttributes(direct, attributes);
final AttributeList mappedAttributes = mBeanServer.getAttributes(mapped, attributes);
final Map<String, BufferPoolStats> stats = new TreeMap<String, BufferPoolStats>();
final BufferPoolStats directStats = new BufferPoolStats((Long) ((Attribute) directAttributes.get(0)).getValue(),
(Long) ((Attribute) directAttributes.get(1)).getValue(),
(Long) ((Attribute) directAttributes.get(2)).getValue());
stats.put("direct", directStats);
final BufferPoolStats mappedStats = new BufferPoolStats((Long) ((Attribute) mappedAttributes.get(0)).getValue(),
(Long) ((Attribute) mappedAttributes.get(1)).getValue(),
(Long) ((Attribute) mappedAttributes.get(2)).getValue());
stats.put("mapped", mappedStats);
return Collections.unmodifiableMap(stats);
} catch (JMException e) {
return Collections.emptyMap();
}
}
}