changeset 4344:4a6facbffc09

8010294: Refactor HeapInspection to make it more reusable Reviewed-by: jwilhelm, brutisso, mgerdin
author ehelin
date Thu, 21 Mar 2013 16:15:57 +0100
parents ad6f90552a1c
children c0b1bfa39232
files src/share/vm/memory/heapInspection.cpp src/share/vm/memory/heapInspection.hpp test/gc/heap_inspection/TestPrintClassHistogram.java
diffstat 3 files changed, 187 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/memory/heapInspection.cpp	Fri Mar 29 10:04:01 2013 -0700
+++ b/src/share/vm/memory/heapInspection.cpp	Thu Mar 21 16:15:57 2013 +0100
@@ -87,7 +87,7 @@
     }
     elt = elt->next();
   }
-  elt = new KlassInfoEntry(k, list());
+  elt = new (std::nothrow) KlassInfoEntry(k, list());
   // We may be out of space to allocate the new entry.
   if (elt != NULL) {
     set_list(elt);
@@ -113,12 +113,12 @@
   }
 }
 
-KlassInfoTable::KlassInfoTable(int size, HeapWord* ref) {
+KlassInfoTable::KlassInfoTable(HeapWord* ref) {
   _size = 0;
   _ref = ref;
-  _buckets = NEW_C_HEAP_ARRAY(KlassInfoBucket, size, mtInternal);
+  _buckets = (KlassInfoBucket *) os::malloc(sizeof(KlassInfoBucket) * _num_buckets, mtInternal);
   if (_buckets != NULL) {
-    _size = size;
+    _size = _num_buckets;
     for (int index = 0; index < _size; index++) {
       _buckets[index].initialize();
     }
@@ -177,9 +177,9 @@
   return (*e1)->compare(*e1,*e2);
 }
 
-KlassInfoHisto::KlassInfoHisto(const char* title, int estimatedCount) :
+KlassInfoHisto::KlassInfoHisto(const char* title) :
   _title(title) {
-  _elements = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<KlassInfoEntry*>(estimatedCount,true);
+  _elements = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<KlassInfoEntry*>(_histo_initial_size, true);
 }
 
 KlassInfoHisto::~KlassInfoHisto() {
@@ -228,82 +228,104 @@
  private:
   KlassInfoTable* _cit;
   size_t _missed_count;
+  BoolObjectClosure* _filter;
  public:
-  RecordInstanceClosure(KlassInfoTable* cit) :
-    _cit(cit), _missed_count(0) {}
+  RecordInstanceClosure(KlassInfoTable* cit, BoolObjectClosure* filter) :
+    _cit(cit), _missed_count(0), _filter(filter) {}
 
   void do_object(oop obj) {
-    if (!_cit->record_instance(obj)) {
-      _missed_count++;
+    if (should_visit(obj)) {
+      if (!_cit->record_instance(obj)) {
+        _missed_count++;
+      }
     }
   }
 
   size_t missed_count() { return _missed_count; }
+ private:
+  bool should_visit(oop obj) {
+    return _filter == NULL || _filter->do_object_b(obj);
+  }
 };
 
+HeapWord* HeapInspection::start_of_perm_gen() {
+  if (is_shared_heap()) {
+    SharedHeap* sh = SharedHeap::heap();
+    return sh->perm_gen()->used_region().start();
+  }
+#ifndef SERIALGC
+  ParallelScavengeHeap* psh = (ParallelScavengeHeap*)Universe::heap();
+  return psh->perm_gen()->object_space()->used_region().start();
+#else
+  ShouldNotReachHere();
+  return NULL;
+#endif // SERIALGC
+}
+
+bool HeapInspection::is_shared_heap() {
+  CollectedHeap* heap = Universe::heap();
+  return heap->kind() == CollectedHeap::G1CollectedHeap ||
+         heap->kind() == CollectedHeap::GenCollectedHeap;
+}
+
+void HeapInspection::prologue() {
+  if (is_shared_heap()) {
+    SharedHeap* sh = SharedHeap::heap();
+    sh->gc_prologue(false /* !full */); // get any necessary locks, etc.
+  }
+}
+
+void HeapInspection::epilogue() {
+  if (is_shared_heap()) {
+    SharedHeap* sh = SharedHeap::heap();
+    sh->gc_epilogue(false /* !full */); // release all acquired locks, etc.
+  }
+}
+
+size_t HeapInspection::instance_inspection(KlassInfoTable* cit,
+                                           KlassInfoClosure* cl,
+                                           bool need_prologue,
+                                           BoolObjectClosure* filter) {
+  ResourceMark rm;
+
+  if (need_prologue) {
+    prologue();
+  }
+
+  RecordInstanceClosure ric(cit, filter);
+  Universe::heap()->object_iterate(&ric);
+  cit->iterate(cl);
+
+  // need to run epilogue if we run prologue
+  if (need_prologue) {
+    epilogue();
+  }
+
+  return ric.missed_count();
+}
+
 void HeapInspection::heap_inspection(outputStream* st, bool need_prologue) {
   ResourceMark rm;
-  HeapWord* ref;
 
-  CollectedHeap* heap = Universe::heap();
-  bool is_shared_heap = false;
-  switch (heap->kind()) {
-    case CollectedHeap::G1CollectedHeap:
-    case CollectedHeap::GenCollectedHeap: {
-      is_shared_heap = true;
-      SharedHeap* sh = (SharedHeap*)heap;
-      if (need_prologue) {
-        sh->gc_prologue(false /* !full */); // get any necessary locks, etc.
-      }
-      ref = sh->perm_gen()->used_region().start();
-      break;
-    }
-#ifndef SERIALGC
-    case CollectedHeap::ParallelScavengeHeap: {
-      ParallelScavengeHeap* psh = (ParallelScavengeHeap*)heap;
-      ref = psh->perm_gen()->object_space()->used_region().start();
-      break;
-    }
-#endif // SERIALGC
-    default:
-      ShouldNotReachHere(); // Unexpected heap kind for this op
-  }
-  // Collect klass instance info
-  KlassInfoTable cit(KlassInfoTable::cit_size, ref);
+  KlassInfoTable cit(start_of_perm_gen());
   if (!cit.allocation_failed()) {
-    // Iterate over objects in the heap
-    RecordInstanceClosure ric(&cit);
-    // If this operation encounters a bad object when using CMS,
-    // consider using safe_object_iterate() which avoids perm gen
-    // objects that may contain bad references.
-    Universe::heap()->object_iterate(&ric);
+    KlassInfoHisto histo("\n"
+                     " num     #instances         #bytes  class name\n"
+                     "----------------------------------------------");
+    HistoClosure hc(&histo);
 
-    // Report if certain classes are not counted because of
-    // running out of C-heap for the histogram.
-    size_t missed_count = ric.missed_count();
+    size_t missed_count = instance_inspection(&cit, &hc, need_prologue);
     if (missed_count != 0) {
       st->print_cr("WARNING: Ran out of C-heap; undercounted " SIZE_FORMAT
                    " total instances in data below",
                    missed_count);
     }
-    // Sort and print klass instance info
-    KlassInfoHisto histo("\n"
-                     " num     #instances         #bytes  class name\n"
-                     "----------------------------------------------",
-                     KlassInfoHisto::histo_initial_size);
-    HistoClosure hc(&histo);
-    cit.iterate(&hc);
     histo.sort();
     histo.print_on(st);
   } else {
     st->print_cr("WARNING: Ran out of C-heap; histogram not generated");
   }
   st->flush();
-
-  if (need_prologue && is_shared_heap) {
-    SharedHeap* sh = (SharedHeap*)heap;
-    sh->gc_epilogue(false /* !full */); // release all acquired locks, etc.
-  }
 }
 
 class FindInstanceClosure : public ObjectClosure {
--- a/src/share/vm/memory/heapInspection.hpp	Fri Mar 29 10:04:01 2013 -0700
+++ b/src/share/vm/memory/heapInspection.hpp	Thu Mar 21 16:15:57 2013 +0100
@@ -85,6 +85,7 @@
 class KlassInfoTable: public StackObj {
  private:
   int _size;
+  static const int _num_buckets = 20011;
 
   // An aligned reference address (typically the least
   // address in the perm gen) used for hashing klass
@@ -96,11 +97,7 @@
   KlassInfoEntry* lookup(const klassOop k);
 
  public:
-  // Table size
-  enum {
-    cit_size = 20011
-  };
-  KlassInfoTable(int size, HeapWord* ref);
+  KlassInfoTable(HeapWord* ref);
   ~KlassInfoTable();
   bool record_instance(const oop obj);
   void iterate(KlassInfoClosure* cic);
@@ -115,12 +112,9 @@
   const char* title() const { return _title; }
   static int sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2);
   void print_elements(outputStream* st) const;
+  static const int _histo_initial_size = 1000;
  public:
-  enum {
-    histo_initial_size = 1000
-  };
-  KlassInfoHisto(const char* title,
-             int estimatedCount);
+  KlassInfoHisto(const char* title);
   ~KlassInfoHisto();
   void add(KlassInfoEntry* cie);
   void print_on(outputStream* st) const;
@@ -131,7 +125,16 @@
 class HeapInspection : public AllStatic {
  public:
   static void heap_inspection(outputStream* st, bool need_prologue);
+  static size_t instance_inspection(KlassInfoTable* cit,
+                                    KlassInfoClosure* cl,
+                                    bool need_prologue,
+                                    BoolObjectClosure* filter = NULL);
+  static HeapWord* start_of_perm_gen();
   static void find_instances_at_safepoint(klassOop k, GrowableArray<oop>* result);
+ private:
+  static bool is_shared_heap();
+  static void prologue();
+  static void epilogue();
 };
 
 #endif // SHARE_VM_MEMORY_HEAPINSPECTION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/gc/heap_inspection/TestPrintClassHistogram.java	Thu Mar 21 16:15:57 2013 +0100
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+/*
+ * @test TestPrintClassHistogram
+ * @bug 8010294
+ * @summary Checks that a class histogram can be printed both before and after
+ *          a GC
+ * @library /testlibrary
+ * @run main/othervm TestPrintClassHistogram launch BeforeGC
+ * @run main/othervm TestPrintClassHistogram launch AfterGC
+ * @run main/othervm TestPrintClassHistogram launch BeforeGC AfterGC
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import com.oracle.java.testlibrary.OutputAnalyzer;
+import com.oracle.java.testlibrary.ProcessTools;
+
+/* This test uses a "trick" to be able to analyze the output of the test:
+ *
+ *   The test starts a java process that runs the same code, but the first
+ *   argument is different ("run" instead of "launch"). The change of flags
+ *   will run the test code instead of launching the test again.
+ */
+public class TestPrintClassHistogram {
+    public static void main(String[] args) throws Exception {
+        if (shouldLaunchTest(args)) {
+            launchTest(args);
+            return;
+        }
+
+        // This is the actual test code
+        System.gc();
+    }
+
+    private static boolean shouldLaunchTest(String[] cmdlineArguments) {
+        return cmdlineArguments[0].equals("launch");
+    }
+
+    private static void launchTest(String[] args) throws Exception {
+        String[] testArgs = createTestArguments(args);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(testArgs);
+
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        output.shouldNotContain("WARNING");
+
+        // There will always be at least one java.lang.Class instance
+        output.shouldContain("java.lang.Class");
+
+        // There will always be at least one java.lang.String instance
+        output.shouldContain("java.lang.String");
+
+        output.shouldHaveExitValue(0);
+    }
+
+    private static String[] createTestArguments(String[] cmdlineArguments) {
+        List<String> cmdlineArgs = Arrays.asList(cmdlineArguments);
+        List<String> testArgs = new ArrayList<String>();
+
+        if (cmdlineArgs.contains("BeforeGC")) {
+            testArgs.add("-XX:+PrintClassHistogramBeforeFullGC");
+        }
+        if (cmdlineArgs.contains("AfterGC")) {
+            testArgs.add("-XX:+PrintClassHistogramAfterFullGC");
+        }
+
+        testArgs.add("TestPrintClassHistogram");
+        testArgs.add("run");
+
+        return testArgs.toArray(new String[testArgs.size()]);
+    }
+}