Author: jbellis
Date: Wed Aug 17 02:20:48 2011
New Revision: 1158490
URL: http://svn.apache.org/viewvc?rev=1158490&view=rev
Log:
work around native memory leak in com.sun.management.GarbageCollectorMXBean
patch by brandonwilliams and jbellis for CASSANDRA-2868
Modified:
cassandra/branches/cassandra-0.8/CHANGES.txt
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/service/GCInspector.java
Modified: cassandra/branches/cassandra-0.8/CHANGES.txt
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/CHANGES.txt?rev=1158490&r1=1158489&r2=1158490&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8/CHANGES.txt Wed Aug 17 02:20:48 2011
@@ -11,6 +11,9 @@
command to the nodetool (CASSANDRA-2991)
* make cleanup and normal compaction able to skip empty rows
(rows containing nothing but expired tombstones) (CASSANDRA-3039)
+ * work around native memory leak in com.sun.management.GarbageCollectorMXBean
+ (CASSANDRA-2868)
+
0.8.4
* include files-to-be-streamed in StreamInSession.getSources (CASSANDRA-2972)
Modified:
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/service/GCInspector.java
URL:
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/service/GCInspector.java?rev=1158490&r1=1158489&r2=1158490&view=diff
==============================================================================
---
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/service/GCInspector.java
(original)
+++
cassandra/branches/cassandra-0.8/src/java/org/apache/cassandra/service/GCInspector.java
Wed Aug 17 02:20:48 2011
@@ -20,11 +20,13 @@ package org.apache.cassandra.service;
*
*/
+import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServer;
import javax.management.ObjectName;
@@ -45,32 +47,22 @@ public class GCInspector
public static final GCInspector instance = new GCInspector();
private HashMap<String, Long> gctimes = new HashMap<String, Long>();
+ private HashMap<String, Long> gccounts = new HashMap<String, Long>();
+
+ List<GarbageCollectorMXBean> beans = new
ArrayList<GarbageCollectorMXBean>();
+ MemoryMXBean membean = ManagementFactory.getMemoryMXBean();
- List<Object> beans = new ArrayList<Object>(); // these are instances of
com.sun.management.GarbageCollectorMXBean
private volatile boolean cacheSizesReduced;
public GCInspector()
{
- // we only want this class to do its thing on sun jdks, or when the
sun classes are present.
- Class gcBeanClass = null;
- try
- {
- gcBeanClass =
Class.forName("com.sun.management.GarbageCollectorMXBean");
- Class.forName("com.sun.management.GcInfo");
- }
- catch (ClassNotFoundException ex)
- {
- // this happens when using a non-sun jdk.
- logger.warn("Cannot load sun GC monitoring classes. GCInspector is
disabled.");
- }
-
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
try
{
ObjectName gcName = new
ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
for (ObjectName name : server.queryNames(gcName, null))
{
- Object gc = ManagementFactory.newPlatformMXBeanProxy(server,
name.getCanonicalName(), gcBeanClass);
+ GarbageCollectorMXBean gc =
ManagementFactory.newPlatformMXBeanProxy(server, name.getCanonicalName(),
GarbageCollectorMXBean.class);
beans.add(gc);
}
}
@@ -97,43 +89,42 @@ public class GCInspector
private void logGCResults()
{
- for (Object gc : beans)
+ for (GarbageCollectorMXBean gc : beans)
{
- SunGcWrapper gcw = new SunGcWrapper(gc);
- if (gcw.isLastGcInfoNull())
+ Long previousTotal = gctimes.get(gc.getName());
+ Long total = gc.getCollectionTime();
+ if (previousTotal == null)
+ previousTotal = 0L;
+ if (previousTotal.equals(total))
continue;
-
- Long previous = gctimes.get(gcw.getName());
- if (previous != null && previous.longValue() ==
gcw.getCollectionTime().longValue())
- continue;
- gctimes.put(gcw.getName(), gcw.getCollectionTime());
-
- long previousMemoryUsed = 0;
- long memoryUsed = 0;
- long memoryMax = 0;
- for (Map.Entry<String, MemoryUsage> entry :
gcw.getMemoryUsageBeforeGc().entrySet())
- {
- previousMemoryUsed += entry.getValue().getUsed();
- }
- for (Map.Entry<String, MemoryUsage> entry :
gcw.getMemoryUsageAfterGc().entrySet())
- {
- MemoryUsage mu = entry.getValue();
- memoryUsed += mu.getUsed();
- memoryMax += mu.getMax();
- }
-
- String st = String.format("GC for %s: %s ms, %s reclaimed leaving
%s used; max is %s",
- gcw.getName(), gcw.getDuration(),
previousMemoryUsed - memoryUsed, memoryUsed, memoryMax);
- if (gcw.getDuration() > MIN_DURATION)
+ gctimes.put(gc.getName(), total);
+ Long duration = total - previousTotal;
+ assert duration > 0;
+
+ Long previousCount = gccounts.get(gc.getName());
+ Long count = gc.getCollectionCount();
+ if (previousCount == null)
+ previousCount = 0L;
+ gccounts.put(gc.getName(), count);
+ assert count > previousCount;
+
+ MemoryUsage mu = membean.getHeapMemoryUsage();
+ long memoryUsed = mu.getUsed();
+ long memoryMax = mu.getMax();
+
+ String st = String.format("GC for %s: %s ms for %s collections, %s
used; max is %s",
+ gc.getName(), duration, count -
previousCount, memoryUsed, memoryMax);
+ long durationPerCollection = duration / (count - previousCount);
+ if (durationPerCollection > MIN_DURATION)
logger.info(st);
else if (logger.isDebugEnabled())
logger.debug(st);
- if (gcw.getDuration() > MIN_DURATION_TPSTATS)
+ if (durationPerCollection > MIN_DURATION_TPSTATS)
StatusLogger.log();
// if we just finished a full collection and we're still using a
lot of memory, try to reduce the pressure
- if (gcw.getName().equals("ConcurrentMarkSweep"))
+ if (gc.getName().equals("ConcurrentMarkSweep"))
{
double usage = (double) memoryUsed / memoryMax;
@@ -152,82 +143,4 @@ public class GCInspector
}
}
}
-
- // wrapper for sun class. this enables other jdks to compile this class.
- private static final class SunGcWrapper
- {
-
- private Map<String, MemoryUsage> usageBeforeGc = null;
- private Map<String, MemoryUsage> usageAfterGc = null;
- private String name;
- private Long collectionTime;
- private Long duration;
-
- SunGcWrapper(Object gcMxBean)
- {
- // if we've gotten this far, we've already verified that the right
classes are in the CP. Now we just
- // need to check for boneheadedness.
- // grab everything we need here so that we don't have to deal with
try/catch everywhere.
- try
- {
- assert
Class.forName("com.sun.management.GarbageCollectorMXBean").isAssignableFrom(gcMxBean.getClass());
- Method getGcInfo =
gcMxBean.getClass().getDeclaredMethod("getLastGcInfo");
- Object lastGcInfo = getGcInfo.invoke(gcMxBean);
- if (lastGcInfo != null)
- {
- usageBeforeGc = (Map<String,
MemoryUsage>)lastGcInfo.getClass().getDeclaredMethod("getMemoryUsageBeforeGc").invoke(lastGcInfo);
- usageAfterGc = (Map<String,
MemoryUsage>)lastGcInfo.getClass().getDeclaredMethod("getMemoryUsageAfterGc").invoke(lastGcInfo);
- duration =
(Long)lastGcInfo.getClass().getDeclaredMethod("getDuration").invoke(lastGcInfo);
- name =
(String)gcMxBean.getClass().getDeclaredMethod("getName").invoke(gcMxBean);
- collectionTime =
(Long)gcMxBean.getClass().getDeclaredMethod("getCollectionTime").invoke(gcMxBean);
- }
- }
- catch (ClassNotFoundException e)
- {
- throw new RuntimeException(e);
- }
- catch (NoSuchMethodException e)
- {
- throw new RuntimeException(e);
- }
- catch (IllegalAccessException e)
- {
- throw new RuntimeException(e);
- }
- catch (InvocationTargetException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- String getName()
- {
- return name;
- }
-
- Long getCollectionTime()
- {
- return collectionTime;
- }
-
- Long getDuration()
- {
- return duration;
- }
-
- Map<String, MemoryUsage> getMemoryUsageAfterGc()
- {
- return usageAfterGc;
- }
-
- Map<String, MemoryUsage> getMemoryUsageBeforeGc()
- {
- return usageBeforeGc;
- }
-
- boolean isLastGcInfoNull()
- {
- return usageBeforeGc == null;
- }
- }
}