8024718: Metaspace performance counters and memory pools should report the same data
authorehelin
Tue, 17 Sep 2013 20:59:07 +0200
changeset 20001 7446501f55bc
parent 20000 7ed3cd9f6793
child 20004 8d5366a97afa
8024718: Metaspace performance counters and memory pools should report the same data Reviewed-by: stefank, dholmes, coleenp
hotspot/src/share/vm/memory/metaspaceCounters.cpp
hotspot/src/share/vm/memory/metaspaceCounters.hpp
hotspot/src/share/vm/services/memoryPool.cpp
hotspot/src/share/vm/services/memoryPool.hpp
hotspot/src/share/vm/services/memoryUsage.hpp
hotspot/test/gc/metaspace/TestMetaspaceMemoryPool.java
hotspot/test/gc/metaspace/TestMetaspacePerfCounters.java
hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java
hotspot/test/testlibrary/com/oracle/java/testlibrary/InputArguments.java
--- a/hotspot/src/share/vm/memory/metaspaceCounters.cpp	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceCounters.cpp	Tue Sep 17 20:59:07 2013 +0200
@@ -65,26 +65,25 @@
 
 MetaspacePerfCounters* MetaspaceCounters::_perf_counters = NULL;
 
-size_t MetaspaceCounters::calculate_capacity() {
-  // The total capacity is the sum of
-  //   1) capacity of Metachunks in use by all Metaspaces
-  //   2) unused space at the end of each Metachunk
-  //   3) space in the freelist
-  size_t total_capacity = MetaspaceAux::allocated_capacity_bytes()
-    + MetaspaceAux::free_bytes() + MetaspaceAux::free_chunks_total_bytes();
-  return total_capacity;
+size_t MetaspaceCounters::used() {
+  return MetaspaceAux::allocated_used_bytes();
+}
+
+size_t MetaspaceCounters::capacity() {
+  return MetaspaceAux::committed_bytes();
+}
+
+size_t MetaspaceCounters::max_capacity() {
+  return MetaspaceAux::reserved_bytes();
 }
 
 void MetaspaceCounters::initialize_performance_counters() {
   if (UsePerfData) {
     assert(_perf_counters == NULL, "Should only be initialized once");
 
-    size_t min_capacity = MetaspaceAux::min_chunk_size_bytes();
-    size_t capacity = calculate_capacity();
-    size_t max_capacity = MetaspaceAux::reserved_bytes();
-    size_t used = MetaspaceAux::allocated_used_bytes();
-
-    _perf_counters = new MetaspacePerfCounters("metaspace", min_capacity, capacity, max_capacity, used);
+    size_t min_capacity = 0;
+    _perf_counters = new MetaspacePerfCounters("metaspace", min_capacity,
+                                               capacity(), max_capacity(), used());
   }
 }
 
@@ -92,31 +91,29 @@
   if (UsePerfData) {
     assert(_perf_counters != NULL, "Should be initialized");
 
-    size_t capacity = calculate_capacity();
-    size_t max_capacity = MetaspaceAux::reserved_bytes();
-    size_t used = MetaspaceAux::allocated_used_bytes();
-
-    _perf_counters->update(capacity, max_capacity, used);
+    _perf_counters->update(capacity(), max_capacity(), used());
   }
 }
 
 MetaspacePerfCounters* CompressedClassSpaceCounters::_perf_counters = NULL;
 
-size_t CompressedClassSpaceCounters::calculate_capacity() {
-    return MetaspaceAux::allocated_capacity_bytes(_class_type) +
-           MetaspaceAux::free_bytes(_class_type) +
-           MetaspaceAux::free_chunks_total_bytes(_class_type);
+size_t CompressedClassSpaceCounters::used() {
+  return MetaspaceAux::allocated_used_bytes(Metaspace::ClassType);
+}
+
+size_t CompressedClassSpaceCounters::capacity() {
+  return MetaspaceAux::committed_bytes(Metaspace::ClassType);
+}
+
+size_t CompressedClassSpaceCounters::max_capacity() {
+  return MetaspaceAux::reserved_bytes(Metaspace::ClassType);
 }
 
 void CompressedClassSpaceCounters::update_performance_counters() {
   if (UsePerfData && UseCompressedClassPointers) {
     assert(_perf_counters != NULL, "Should be initialized");
 
-    size_t capacity = calculate_capacity();
-    size_t max_capacity = MetaspaceAux::reserved_bytes(_class_type);
-    size_t used = MetaspaceAux::allocated_used_bytes(_class_type);
-
-    _perf_counters->update(capacity, max_capacity, used);
+    _perf_counters->update(capacity(), max_capacity(), used());
   }
 }
 
@@ -126,12 +123,9 @@
     const char* ns = "compressedclassspace";
 
     if (UseCompressedClassPointers) {
-      size_t min_capacity = MetaspaceAux::min_chunk_size_bytes();
-      size_t capacity = calculate_capacity();
-      size_t max_capacity = MetaspaceAux::reserved_bytes(_class_type);
-      size_t used = MetaspaceAux::allocated_used_bytes(_class_type);
-
-      _perf_counters = new MetaspacePerfCounters(ns, min_capacity, capacity, max_capacity, used);
+      size_t min_capacity = 0;
+      _perf_counters = new MetaspacePerfCounters(ns, min_capacity, capacity(),
+                                                 max_capacity(), used());
     } else {
       _perf_counters = new MetaspacePerfCounters(ns, 0, 0, 0, 0);
     }
--- a/hotspot/src/share/vm/memory/metaspaceCounters.hpp	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceCounters.hpp	Tue Sep 17 20:59:07 2013 +0200
@@ -25,13 +25,15 @@
 #ifndef SHARE_VM_MEMORY_METASPACECOUNTERS_HPP
 #define SHARE_VM_MEMORY_METASPACECOUNTERS_HPP
 
-#include "memory/metaspace.hpp"
+#include "memory/allocation.hpp"
 
 class MetaspacePerfCounters;
 
 class MetaspaceCounters: public AllStatic {
   static MetaspacePerfCounters* _perf_counters;
-  static size_t calculate_capacity();
+  static size_t used();
+  static size_t capacity();
+  static size_t max_capacity();
 
  public:
   static void initialize_performance_counters();
@@ -40,8 +42,9 @@
 
 class CompressedClassSpaceCounters: public AllStatic {
   static MetaspacePerfCounters* _perf_counters;
-  static size_t calculate_capacity();
-  static const Metaspace::MetadataType _class_type = Metaspace::ClassType;
+  static size_t used();
+  static size_t capacity();
+  static size_t max_capacity();
 
  public:
   static void initialize_performance_counters();
--- a/hotspot/src/share/vm/services/memoryPool.cpp	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/src/share/vm/services/memoryPool.cpp	Tue Sep 17 20:59:07 2013 +0200
@@ -260,10 +260,10 @@
 }
 
 MetaspacePool::MetaspacePool() :
-  MemoryPool("Metaspace", NonHeap, capacity_in_bytes(), calculate_max_size(), true, false) { }
+  MemoryPool("Metaspace", NonHeap, 0, calculate_max_size(), true, false) { }
 
 MemoryUsage MetaspacePool::get_memory_usage() {
-  size_t committed = align_size_down_(capacity_in_bytes(), os::vm_page_size());
+  size_t committed = MetaspaceAux::committed_bytes();
   return MemoryUsage(initial_size(), used_in_bytes(), committed, max_size());
 }
 
@@ -271,26 +271,19 @@
   return MetaspaceAux::allocated_used_bytes();
 }
 
-size_t MetaspacePool::capacity_in_bytes() const {
-  return MetaspaceAux::allocated_capacity_bytes();
-}
-
 size_t MetaspacePool::calculate_max_size() const {
-  return FLAG_IS_CMDLINE(MaxMetaspaceSize) ? MaxMetaspaceSize : max_uintx;
+  return FLAG_IS_CMDLINE(MaxMetaspaceSize) ? MaxMetaspaceSize :
+                                             MemoryUsage::undefined_size();
 }
 
 CompressedKlassSpacePool::CompressedKlassSpacePool() :
-  MemoryPool("Compressed Class Space", NonHeap, capacity_in_bytes(), CompressedClassSpaceSize, true, false) { }
+  MemoryPool("Compressed Class Space", NonHeap, 0, CompressedClassSpaceSize, true, false) { }
 
 size_t CompressedKlassSpacePool::used_in_bytes() {
   return MetaspaceAux::allocated_used_bytes(Metaspace::ClassType);
 }
 
-size_t CompressedKlassSpacePool::capacity_in_bytes() const {
-  return MetaspaceAux::allocated_capacity_bytes(Metaspace::ClassType);
-}
-
 MemoryUsage CompressedKlassSpacePool::get_memory_usage() {
-  size_t committed = align_size_down_(capacity_in_bytes(), os::vm_page_size());
+  size_t committed = MetaspaceAux::committed_bytes(Metaspace::ClassType);
   return MemoryUsage(initial_size(), used_in_bytes(), committed, max_size());
 }
--- a/hotspot/src/share/vm/services/memoryPool.hpp	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/src/share/vm/services/memoryPool.hpp	Tue Sep 17 20:59:07 2013 +0200
@@ -224,7 +224,6 @@
 
 class MetaspacePool : public MemoryPool {
   size_t calculate_max_size() const;
-  size_t capacity_in_bytes() const;
  public:
   MetaspacePool();
   MemoryUsage get_memory_usage();
@@ -232,7 +231,6 @@
 };
 
 class CompressedKlassSpacePool : public MemoryPool {
-  size_t capacity_in_bytes() const;
  public:
   CompressedKlassSpacePool();
   MemoryUsage get_memory_usage();
--- a/hotspot/src/share/vm/services/memoryUsage.hpp	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/src/share/vm/services/memoryUsage.hpp	Tue Sep 17 20:59:07 2013 +0200
@@ -63,10 +63,12 @@
   size_t committed() const { return _committed; }
   size_t max_size()  const { return _maxSize; }
 
+  static size_t undefined_size() { return (size_t) -1; }
+
   inline static jlong convert_to_jlong(size_t val) {
     // In the 64-bit vm, a size_t can overflow a jlong (which is signed).
     jlong ret;
-    if (val == (size_t)-1) {
+    if (val == undefined_size()) {
       ret = -1L;
     } else {
       NOT_LP64(ret = val;)
--- a/hotspot/test/gc/metaspace/TestMetaspaceMemoryPool.java	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/test/gc/metaspace/TestMetaspaceMemoryPool.java	Tue Sep 17 20:59:07 2013 +0200
@@ -22,18 +22,15 @@
  */
 
 import java.util.List;
-import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryManagerMXBean;
-import java.lang.management.MemoryPoolMXBean;
-import java.lang.management.MemoryUsage;
-
-import java.lang.management.RuntimeMXBean;
-import java.lang.management.ManagementFactory;
+import java.lang.management.*;
+import com.oracle.java.testlibrary.*;
+import static com.oracle.java.testlibrary.Asserts.*;
 
 /* @test TestMetaspaceMemoryPool
  * @bug 8000754
  * @summary Tests that a MemoryPoolMXBeans is created for metaspace and that a
  *          MemoryManagerMXBean is created.
+ * @library /testlibrary
  * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseCompressedOops TestMetaspaceMemoryPool
  * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseCompressedOops -XX:MaxMetaspaceSize=60m TestMetaspaceMemoryPool
  * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCompressedOops -XX:+UseCompressedClassPointers TestMetaspaceMemoryPool
@@ -42,35 +39,18 @@
 public class TestMetaspaceMemoryPool {
     public static void main(String[] args) {
         verifyThatMetaspaceMemoryManagerExists();
-        verifyMemoryPool(getMemoryPool("Metaspace"), isFlagDefined("MaxMetaspaceSize"));
 
-        if (runsOn64bit()) {
-            if (usesCompressedOops()) {
+        boolean isMetaspaceMaxDefined = InputArguments.containsPrefix("-XX:MaxMetaspaceSize");
+        verifyMemoryPool(getMemoryPool("Metaspace"), isMetaspaceMaxDefined);
+
+        if (Platform.is64bit()) {
+            if (InputArguments.contains("-XX:+UseCompressedOops")) {
                 MemoryPoolMXBean cksPool = getMemoryPool("Compressed Class Space");
                 verifyMemoryPool(cksPool, true);
             }
         }
     }
 
-    private static boolean runsOn64bit() {
-        return !System.getProperty("sun.arch.data.model").equals("32");
-    }
-
-    private static boolean usesCompressedOops() {
-        return isFlagDefined("+UseCompressedOops");
-    }
-
-    private static boolean isFlagDefined(String name) {
-        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
-        List<String> args = runtimeMxBean.getInputArguments();
-        for (String arg : args) {
-            if (arg.startsWith("-XX:" + name)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private static void verifyThatMetaspaceMemoryManagerExists() {
         List<MemoryManagerMXBean> managers = ManagementFactory.getMemoryManagerMXBeans();
         for (MemoryManagerMXBean manager : managers) {
@@ -95,32 +75,19 @@
 
     private static void verifyMemoryPool(MemoryPoolMXBean pool, boolean isMaxDefined) {
         MemoryUsage mu = pool.getUsage();
-        assertDefined(mu.getInit(), "init");
-        assertDefined(mu.getUsed(), "used");
-        assertDefined(mu.getCommitted(), "committed");
+        long init = mu.getInit();
+        long used = mu.getUsed();
+        long committed = mu.getCommitted();
+        long max = mu.getMax();
+
+        assertGTE(init, 0L);
+        assertGTE(used, init);
+        assertGTE(committed, used);
 
         if (isMaxDefined) {
-            assertDefined(mu.getMax(), "max");
+            assertGTE(max, committed);
         } else {
-            assertUndefined(mu.getMax(), "max");
-        }
-    }
-
-    private static void assertDefined(long value, String name) {
-        assertTrue(value != -1, "Expected " + name + " to be defined");
-    }
-
-    private static void assertUndefined(long value, String name) {
-        assertEquals(value, -1, "Expected " + name + " to be undefined");
-    }
-
-    private static void assertEquals(long actual, long expected, String msg) {
-        assertTrue(actual == expected, msg);
-    }
-
-    private static void assertTrue(boolean condition, String msg) {
-        if (!condition) {
-            throw new RuntimeException(msg);
+            assertEQ(max, -1L);
         }
     }
 }
--- a/hotspot/test/gc/metaspace/TestMetaspacePerfCounters.java	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/test/gc/metaspace/TestMetaspacePerfCounters.java	Tue Sep 17 20:59:07 2013 +0200
@@ -61,10 +61,15 @@
     }
 
     private static void checkPerfCounters(String ns) throws Exception {
-        for (PerfCounter counter : countersInNamespace(ns)) {
-            String msg = "Expected " + counter.getName() + " to be larger than 0";
-            assertGT(counter.longValue(), 0L, msg);
-        }
+        long minCapacity = getMinCapacity(ns);
+        long maxCapacity = getMaxCapacity(ns);
+        long capacity = getCapacity(ns);
+        long used = getUsed(ns);
+
+        assertGTE(minCapacity, 0L);
+        assertGTE(used, minCapacity);
+        assertGTE(capacity, used);
+        assertGTE(maxCapacity, capacity);
     }
 
     private static void checkEmptyPerfCounters(String ns) throws Exception {
@@ -75,12 +80,10 @@
     }
 
     private static void checkUsedIncreasesWhenLoadingClass(String ns) throws Exception {
-        PerfCounter used = PerfCounters.findByName(ns + ".used");
-
-        long before = used.longValue();
+        long before = getUsed(ns);
         fooClass = compileAndLoad("Foo", "public class Foo { }");
         System.gc();
-        long after = used.longValue();
+        long after = getUsed(ns);
 
         assertGT(after, before);
     }
@@ -101,4 +104,20 @@
     private static boolean isUsingCompressedClassPointers() {
         return Platform.is64bit() && InputArguments.contains("-XX:+UseCompressedClassPointers");
     }
+
+    private static long getMinCapacity(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".minCapacity").longValue();
+    }
+
+    private static long getCapacity(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".capacity").longValue();
+    }
+
+    private static long getMaxCapacity(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".maxCapacity").longValue();
+    }
+
+    private static long getUsed(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".used").longValue();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/metaspace/TestPerfCountersAndMemoryPools.java	Tue Sep 17 20:59:07 2013 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.List;
+import java.lang.management.*;
+
+import com.oracle.java.testlibrary.*;
+import static com.oracle.java.testlibrary.Asserts.*;
+
+/* @test TestPerfCountersAndMemoryPools
+ * @bug 8023476
+ * @summary Tests that a MemoryPoolMXBeans and PerfCounters for metaspace
+ *          report the same data.
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseCompressedOops -XX:-UseCompressedKlassPointers -XX:+UseSerialGC -XX:+UsePerfData TestPerfCountersAndMemoryPools
+ * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCompressedOops -XX:+UseCompressedKlassPointers -XX:+UseSerialGC -XX:+UsePerfData TestPerfCountersAndMemoryPools
+ */
+public class TestPerfCountersAndMemoryPools {
+    public static void main(String[] args) throws Exception {
+        checkMemoryUsage("Metaspace", "sun.gc.metaspace");
+
+        if (InputArguments.contains("-XX:+UseCompressedKlassPointers") && Platform.is64bit()) {
+            checkMemoryUsage("Compressed Class Space", "sun.gc.compressedclassspace");
+        }
+    }
+
+    private static MemoryUsage getMemoryUsage(String memoryPoolName) {
+        List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
+        for (MemoryPoolMXBean pool : pools) {
+            if (pool.getName().equals(memoryPoolName)) {
+                return pool.getUsage();
+            }
+        }
+
+        throw new RuntimeException("Excpted to find a memory pool with name " +
+                                   memoryPoolName);
+    }
+
+    private static void checkMemoryUsage(String memoryPoolName, String perfNS)
+        throws Exception {
+        // Need to do a gc before each comparison to update the perf counters
+
+        System.gc();
+        MemoryUsage mu = getMemoryUsage(memoryPoolName);
+        assertEQ(getMinCapacity(perfNS), mu.getInit());
+
+        System.gc();
+        mu = getMemoryUsage(memoryPoolName);
+        assertEQ(getUsed(perfNS), mu.getUsed());
+
+        System.gc();
+        mu = getMemoryUsage(memoryPoolName);
+        assertEQ(getCapacity(perfNS), mu.getCommitted());
+    }
+
+    private static long getMinCapacity(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".minCapacity").longValue();
+    }
+
+    private static long getCapacity(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".capacity").longValue();
+    }
+
+    private static long getUsed(String ns) throws Exception {
+        return PerfCounters.findByName(ns + ".used").longValue();
+    }
+}
--- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/InputArguments.java	Wed Sep 18 00:08:00 2013 +0000
+++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/InputArguments.java	Tue Sep 17 20:59:07 2013 +0200
@@ -41,6 +41,9 @@
     /**
      * Returns true if {@code arg} is an input argument to the VM.
      *
+     * This is useful for checking boolean flags such as -XX:+UseSerialGC or
+     * -XX:-UsePerfData.
+     *
      * @param arg The name of the argument.
      * @return {@code true} if the given argument is an input argument,
      *         otherwise {@code false}.
@@ -48,4 +51,26 @@
     public static boolean contains(String arg) {
         return args.contains(arg);
     }
+
+    /**
+     * Returns true if {@code prefix} is the start of an input argument to the
+     * VM.
+     *
+     * This is useful for checking if flags describing a quantity, such as
+     * -XX:+MaxMetaspaceSize=100m, is set without having to know the quantity.
+     * To check if the flag -XX:MaxMetaspaceSize is set, use
+     * {@code InputArguments.containsPrefix("-XX:MaxMetaspaceSize")}.
+     *
+     * @param prefix The start of the argument.
+     * @return {@code true} if the given argument is the start of an input
+     *         argument, otherwise {@code false}.
+     */
+    public static boolean containsPrefix(String prefix) {
+        for (String arg : args) {
+            if (arg.startsWith(prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }