--- old/src/share/vm/memory/heapInspection.cpp 2013-03-22 09:00:06.782070942 +0100 +++ new/src/share/vm/memory/heapInspection.cpp 2013-03-22 09:00:06.714070944 +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(estimatedCount,true); + _elements = new (ResourceObj::C_HEAP, mtInternal) GrowableArray(_histo_initial_size,true); } KlassInfoHisto::~KlassInfoHisto() { @@ -228,82 +228,103 @@ 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); + } }; -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; - } +HeapWord* HeapInspection::start_of_perm_gen() { + if (is_shared_heap()) { + SharedHeap* sh = (SharedHeap*)Universe::heap(); + return sh->perm_gen()->used_region().start(); + } #ifndef SERIALGC - case CollectedHeap::ParallelScavengeHeap: { - ParallelScavengeHeap* psh = (ParallelScavengeHeap*)heap; - ref = psh->perm_gen()->object_space()->used_region().start(); - break; - } + ParallelScavengeHeap* psh = (ParallelScavengeHeap*)Universe::heap(); + return psh->perm_gen()->object_space()->used_region().start(); #endif // SERIALGC - default: - ShouldNotReachHere(); // Unexpected heap kind for this op + ShouldNotReachHere(); + return NULL; +} + +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*)Universe::heap(); + sh->gc_prologue(false /* !full */); // get any necessary locks, etc. + } +} + +void HeapInspection::epilogue() { + if (is_shared_heap()) { + SharedHeap* sh = (SharedHeap*)Universe::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); + + bool need_epilogue = need_prologue; // need to run epilogue if we run prologue + if (need_epilogue) { + epilogue(); } - // Collect klass instance info - KlassInfoTable cit(KlassInfoTable::cit_size, ref); + + return ric.missed_count(); +} + +void HeapInspection::heap_inspection(outputStream* st, bool need_prologue) { + ResourceMark rm; + + 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); - - // Report if certain classes are not counted because of - // running out of C-heap for the histogram. - size_t missed_count = ric.missed_count(); - if (missed_count != 0) { + KlassInfoHisto histo("\n" + " num #instances #bytes class name\n" + "----------------------------------------------"); + HistoClosure hc(&histo); + + 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 { --- old/src/share/vm/memory/heapInspection.hpp 2013-03-22 09:00:07.126070937 +0100 +++ new/src/share/vm/memory/heapInspection.hpp 2013-03-22 09:00:07.066070938 +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* result); + private: + static bool is_shared_heap(); + static void prologue(); + static void epilogue(); }; #endif // SHARE_VM_MEMORY_HEAPINSPECTION_HPP --- /dev/null 2013-03-22 08:32:14.894096845 +0100 +++ new/test/gc/heap_inspection/TestPrintClassHistogram.java 2013-03-22 09:00:07.406070933 +0100 @@ -0,0 +1,77 @@ +/* + * 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 8004924 + * @summary Checks that jmap -heap contains the flag ClassMetaspaceSize + * @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; + +public class TestPrintClassHistogram { + public static void main(String[] args) throws Exception { + if (shouldLaunchTest(args)) { + String[] testArgs = createTestArguments(args); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(testArgs); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldNotContain("WARNING"); + output.shouldContain("java.lang.Class"); // there will always be at least one java.lang.Class instance + output.shouldContain("java.lang.String"); // there will always be at least one java.lang.String instance + output.shouldHaveExitValue(0); + } else { + System.gc(); + } + } + + private static boolean shouldLaunchTest(String[] cmdlineArguments) { + return cmdlineArguments[0].equals("launch"); + } + + private static String[] createTestArguments(String[] cmdlineArguments) { + List cmdlineArgs = Arrays.asList(cmdlineArguments); + List testArgs = new ArrayList(); + + if (cmdlineArgs.contains("BeforeGC")) { + testArgs.add("-XX:+PrintClassHistogramBeforeFullGC"); + } + if (cmdlineArgs.contains("AfterGC")) { + testArgs.add("-XX:+PrintClassHistogramBeforeFullGC"); + } + + testArgs.add("TestPrintClassHistogram"); + testArgs.add("run"); + + return testArgs.toArray(new String[testArgs.size()]); + } +}