Author: brandonwilliams
Date: Tue Aug 23 20:05:38 2011
New Revision: 1160879

URL: http://svn.apache.org/viewvc?rev=1160879&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.7/CHANGES.txt
    
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/service/GCInspector.java

Modified: cassandra/branches/cassandra-0.7/CHANGES.txt
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/CHANGES.txt?rev=1160879&r1=1160878&r2=1160879&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.7/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.7/CHANGES.txt Tue Aug 23 20:05:38 2011
@@ -8,6 +8,8 @@
  * avoid retaining references to dropped CFS objects in 
    CompactionManager.estimatedCompactions (CASSANDRA-2708)
  * remove gossip state when a new IP takes over a token (CASSANDRA-3071)
+ * work around native memory leak in com.sun.management.GarbageCollectorMXBean
+    (CASSANDRA-2868)
 
 
 0.7.8

Modified: 
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/service/GCInspector.java
URL: 
http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/service/GCInspector.java?rev=1160879&r1=1160878&r2=1160879&view=diff
==============================================================================
--- 
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/service/GCInspector.java
 (original)
+++ 
cassandra/branches/cassandra-0.7/src/java/org/apache/cassandra/service/GCInspector.java
 Tue Aug 23 20:05:38 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;
-        }
-    }
 }


Reply via email to