package org.gridkit.jvmtool.gcflow; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import javax.management.JMX; import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.gridkit.jvmtool.gcflow.GarbageCollectionSampler.GcReport; import org.gridkit.jvmtool.gcflow.GcKnowledgeBase.PoolType; import com.sun.management.GarbageCollectorMXBean; import com.sun.management.GcInfo; @SuppressWarnings("restriction") class GcAdapter { private final GarbageCollectionSampler sampler; private final GarbageCollectorMXBean gc; private final String name; private final long processStartMs; private final List<String> collectedPools; private final List<String> allCollectedPools; private final List<String> edenPools; private final List<String> survivourPools; private final List<String> youngPools; private final List<String> oldPools; private final List<String> permPools; private final boolean isYoung; private final boolean isConcurent; private long gcCount = -1; private long prevCollectionEndTime = -1; public GcAdapter(MBeanServerConnection mserver, ObjectName gcname, GarbageCollectionSampler sampler) throws IOException, MalformedObjectNameException { this.sampler = sampler; gc = JMX.newMXBeanProxy(mserver, gcname, GarbageCollectorMXBean.class); name = gc.getName(); RuntimeMXBean runtime = JMX.newMXBeanProxy(mserver, new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME), RuntimeMXBean.class); processStartMs = runtime.getStartTime(); collectedPools = Arrays.asList(gc.getMemoryPoolNames()); allCollectedPools = new ArrayList<String>(GcKnowledgeBase.allCollectedPools(mserver)); Map<GcKnowledgeBase.PoolType, Collection<String>> types = GcKnowledgeBase.classifyMemoryPools(mserver); edenPools = getMemPools(types, PoolType.EDEN); survivourPools = getMemPools(types, PoolType.SURVIVOR); oldPools = getMemPools(types, PoolType.TENURED); permPools = getMemPools(types, PoolType.PERMANENT); youngPools = new ArrayList<String>(); youngPools.addAll(edenPools); youngPools.addAll(survivourPools); isYoung = collectedPools.containsAll(oldPools); isConcurent = "ConcurrentMarkSweep".equals(name); } private List<String> getMemPools(Map<PoolType, Collection<String>> types, PoolType type) { List<String> pools; if (types.containsKey(type)) { pools = new ArrayList<String>(types.get(type)); } else { pools = Collections.emptyList(); } return pools; } public void report() { try { GcInfo lastGc = gc.getLastGcInfo(); if (lastGc == null || lastGc.getId() == gcCount) { return; } else { int missed = (int)(lastGc.getId() - 1 - gcCount); if (gcCount < 0) { missed = 0; } long gcInterval = lastGc.getStartTime() - prevCollectionEndTime; prevCollectionEndTime = lastGc.getEndTime(); if (gcCount < 0) { gcInterval = -1; } if (lastGc.getEndTime() == 0) { // no GC so far prevCollectionEndTime = 0; gcCount = lastGc.getId(); } else { gcCount = lastGc.getId(); sampler.report(name, missed, new Report(lastGc, gcInterval)); } } } catch (Exception e) { throw new RuntimeException(e); } } private class Report implements GcReport { private GcInfo gcInfo; private long gcInterval; public Report(GcInfo gcInfo, long gcInterval) { this.gcInfo = gcInfo; this.gcInterval = gcInterval; } @Override public long getId() { return gcInfo.getId(); } @Override public long getWallClockStartTime() { return processStartMs + gcInfo.getStartTime(); } @Override public long getWallClockEndTime() { return processStartMs + gcInfo.getEndTime(); } @Override public long getJvmClockStartTime() { return gcInfo.getStartTime(); } @Override public long getJvmClockEndTime() { return gcInfo.getEndTime(); } @Override public long getDuration() { return gcInfo.getDuration(); } @Override public long getTimeSincePreviousGC() { return gcInterval; } @Override public boolean isYoungGC() { return isYoung; } @Override public boolean isConcurrentGC() { return isConcurent; } @Override public long getCollectedSize() { return getTotalSizeBefore() - getTotalSizeAfter(); } @Override public long getPromotedSize() { return getSizeAfter(oldPools) - getSizeBefore(oldPools); } @Override public long getTotalSizeBefore() { return getSizeBefore(allCollectedPools); } @Override public long getTotalSizeAfter() { return getSizeAfter(allCollectedPools); } @Override public Collection<String> getColletedPools() { return Collections.unmodifiableCollection(collectedPools); } @Override public Collection<String> getAllCollectedPools() { return Collections.unmodifiableCollection(allCollectedPools); } @Override public Collection<String> getAllMemoryPools() { return Collections.unmodifiableCollection(gcInfo.getMemoryUsageAfterGc().keySet()); } @Override public long getSizeBefore(String pool) { return gcInfo.getMemoryUsageBeforeGc().get(pool).getUsed(); } @Override public long getSizeAfter(String pool) { return gcInfo.getMemoryUsageAfterGc().get(pool).getUsed(); } @Override public long getSizeBefore(Collection<String> pools) { long total = 0; for(String pool: pools) { total += getSizeBefore(pool); } return total; } @Override public long getSizeAfter(Collection<String> pools) { long total = 0; for(String pool: pools) { total += getSizeAfter(pool); } return total; } @Override public Collection<String> getEdenPools() { List<String> list = new ArrayList<String>(edenPools); list.retainAll(gcInfo.getMemoryUsageAfterGc().keySet()); return list; } @Override public Collection<String> getSurvivourPools() { List<String> list = new ArrayList<String>(survivourPools); list.retainAll(gcInfo.getMemoryUsageAfterGc().keySet()); return list; } @Override public Collection<String> getOldSpacePools() { List<String> list = new ArrayList<String>(oldPools); list.retainAll(gcInfo.getMemoryUsageAfterGc().keySet()); return list; } @Override public Collection<String> getPermSpacePools() { List<String> list = new ArrayList<String>(permPools); list.retainAll(gcInfo.getMemoryUsageAfterGc().keySet()); return list; } } }