6479360: PrintClassHistogram improvements
authoracorn
Fri, 25 Jan 2013 15:06:18 -0500
changeset 15437 eabd4555d072
parent 15431 570c5062ab8a
child 15438 2d1c179e9b5c
6479360: PrintClassHistogram improvements Summary: jcmd <pid> GC.class_stats (UnlockDiagnosticVMOptions) Reviewed-by: coleenp, hseigel, sla, acorn Contributed-by: ioi.lam@oracle.com
hotspot/src/share/vm/classfile/classLoaderData.cpp
hotspot/src/share/vm/classfile/classLoaderData.hpp
hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp
hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.hpp
hotspot/src/share/vm/memory/heapInspection.cpp
hotspot/src/share/vm/memory/heapInspection.hpp
hotspot/src/share/vm/oops/annotations.cpp
hotspot/src/share/vm/oops/annotations.hpp
hotspot/src/share/vm/oops/arrayKlass.hpp
hotspot/src/share/vm/oops/constMethod.cpp
hotspot/src/share/vm/oops/constMethod.hpp
hotspot/src/share/vm/oops/constantPool.cpp
hotspot/src/share/vm/oops/constantPool.hpp
hotspot/src/share/vm/oops/instanceKlass.cpp
hotspot/src/share/vm/oops/instanceKlass.hpp
hotspot/src/share/vm/oops/klass.cpp
hotspot/src/share/vm/oops/klass.hpp
hotspot/src/share/vm/oops/method.cpp
hotspot/src/share/vm/oops/method.hpp
hotspot/src/share/vm/oops/methodData.cpp
hotspot/src/share/vm/oops/methodData.hpp
hotspot/src/share/vm/services/diagnosticCommand.cpp
hotspot/src/share/vm/services/diagnosticCommand.hpp
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -724,13 +724,13 @@
   }
   MetaspaceAux::dump(out);
 }
+#endif // PRODUCT
 
 void ClassLoaderData::print_value_on(outputStream* out) const {
   if (class_loader() == NULL) {
-    out->print_cr("NULL class_loader");
+    out->print("NULL class_loader");
   } else {
     out->print("class loader "PTR_FORMAT, this);
     class_loader()->print_value_on(out);
   }
 }
-#endif // PRODUCT
--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -220,7 +220,7 @@
   void set_jmethod_ids(JNIMethodBlock* new_block)  { _jmethod_ids = new_block; }
 
   void print_value() { print_value_on(tty); }
-  void print_value_on(outputStream* out) const PRODUCT_RETURN;
+  void print_value_on(outputStream* out) const;
   void dump(outputStream * const out) PRODUCT_RETURN;
   void verify();
   const char* loader_name();
--- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -167,7 +167,9 @@
       ch->collect_as_vm_thread(GCCause::_heap_inspection);
     }
   }
-  HeapInspection::heap_inspection(_out, _need_prologue /* need_prologue */);
+  HeapInspection inspect(_csv_format, _print_help, _print_class_stats,
+                         _columns);
+  inspect.heap_inspection(_out, _need_prologue /* need_prologue */);
 }
 
 
--- a/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/gc_implementation/shared/vmGCOperations.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -130,6 +130,10 @@
   outputStream* _out;
   bool _full_gc;
   bool _need_prologue;
+  bool _csv_format; // "comma separated values" format for spreadsheet.
+  bool _print_help;
+  bool _print_class_stats;
+  const char* _columns;
  public:
   VM_GC_HeapInspection(outputStream* out, bool request_full_gc,
                        bool need_prologue) :
@@ -140,6 +144,10 @@
     _out = out;
     _full_gc = request_full_gc;
     _need_prologue = need_prologue;
+    _csv_format = false;
+    _print_help = false;
+    _print_class_stats = false;
+    _columns = NULL;
   }
 
   ~VM_GC_HeapInspection() {}
@@ -147,6 +155,10 @@
   virtual bool skip_operation() const;
   virtual bool doit_prologue();
   virtual void doit();
+  void set_csv_format(bool value) {_csv_format = value;}
+  void set_print_help(bool value) {_print_help = value;}
+  void set_print_class_stats(bool value) {_print_class_stats = value;}
+  void set_columns(const char* value) {_columns = value;}
 };
 
 
--- a/hotspot/src/share/vm/memory/heapInspection.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/memory/heapInspection.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/classLoaderData.hpp"
 #include "gc_interface/collectedHeap.hpp"
 #include "memory/genCollectedHeap.hpp"
 #include "memory/heapInspection.hpp"
@@ -41,12 +42,24 @@
   } else if(e1->_instance_words < e2->_instance_words) {
     return 1;
   }
-  return 0;
+  // Sort alphabetically, note 'Z' < '[' < 'a', but it's better to group
+  // the array classes before all the instance classes.
+  ResourceMark rm;
+  const char* name1 = e1->klass()->external_name();
+  const char* name2 = e2->klass()->external_name();
+  bool d1 = (name1[0] == '[');
+  bool d2 = (name2[0] == '[');
+  if (d1 && !d2) {
+    return -1;
+  } else if (d2 && !d1) {
+    return 1;
+  } else {
+    return strcmp(name1, name2);
+  }
 }
 
-void KlassInfoEntry::print_on(outputStream* st) const {
-  ResourceMark rm;
-  const char* name;;
+const char* KlassInfoEntry::name() const {
+  const char* name;
   if (_klass->name() != NULL) {
     name = _klass->external_name();
   } else {
@@ -60,11 +73,17 @@
     if (_klass == Universe::longArrayKlassObj())         name = "<longArrayKlass>";         else
       name = "<no name>";
   }
+  return name;
+}
+
+void KlassInfoEntry::print_on(outputStream* st) const {
+  ResourceMark rm;
+
   // simplify the formatting (ILP32 vs LP64) - always cast the numbers to 64-bit
   st->print_cr(INT64_FORMAT_W(13) "  " UINT64_FORMAT_W(13) "  %s",
                (jlong)  _instance_count,
                (julong) _instance_words * HeapWordSize,
-               name);
+               name());
 }
 
 KlassInfoEntry* KlassInfoBucket::lookup(Klass* const k) {
@@ -101,7 +120,14 @@
   }
 }
 
-KlassInfoTable::KlassInfoTable(int size, HeapWord* ref) {
+void KlassInfoTable::AllClassesFinder::do_klass(Klass* k) {
+  // This has the SIDE EFFECT of creating a KlassInfoEntry
+  // for <k>, if one doesn't exist yet.
+  _table->lookup(k);
+}
+
+KlassInfoTable::KlassInfoTable(int size, HeapWord* ref,
+                               bool need_class_stats) {
   _size = 0;
   _ref = ref;
   _buckets = NEW_C_HEAP_ARRAY(KlassInfoBucket, size, mtInternal);
@@ -110,6 +136,10 @@
     for (int index = 0; index < _size; index++) {
       _buckets[index].initialize();
     }
+    if (need_class_stats) {
+      AllClassesFinder finder(this);
+      ClassLoaderDataGraph::classes_do(&finder);
+    }
   }
 }
 
@@ -165,7 +195,8 @@
   return (*e1)->compare(*e1,*e2);
 }
 
-KlassInfoHisto::KlassInfoHisto(const char* title, int estimatedCount) :
+KlassInfoHisto::KlassInfoHisto(KlassInfoTable* cit, const char* title, int estimatedCount) :
+  _cit(cit),
   _title(title) {
   _elements = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<KlassInfoEntry*>(estimatedCount,true);
 }
@@ -196,9 +227,205 @@
                total, totalw * HeapWordSize);
 }
 
-void KlassInfoHisto::print_on(outputStream* st) const {
-  st->print_cr("%s",title());
-  print_elements(st);
+#define MAKE_COL_NAME(field, name, help)     #name,
+#define MAKE_COL_HELP(field, name, help)     help,
+
+static const char *name_table[] = {
+  HEAP_INSPECTION_COLUMNS_DO(MAKE_COL_NAME)
+};
+
+static const char *help_table[] = {
+  HEAP_INSPECTION_COLUMNS_DO(MAKE_COL_HELP)
+};
+
+bool KlassInfoHisto::is_selected(const char *col_name) {
+  if (_selected_columns == NULL) {
+    return true;
+  }
+  if (strcmp(_selected_columns, col_name) == 0) {
+    return true;
+  }
+
+  const char *start = strstr(_selected_columns, col_name);
+  if (start == NULL) {
+    return false;
+  }
+
+  // The following must be true, because _selected_columns != col_name
+  if (start > _selected_columns && start[-1] != ',') {
+    return false;
+  }
+  char x = start[strlen(col_name)];
+  if (x != ',' && x != '\0') {
+    return false;
+  }
+
+  return true;
+}
+
+void KlassInfoHisto::print_title(outputStream* st, bool csv_format,
+                                 bool selected[], int width_table[],
+                                 const char *name_table[]) {
+  if (csv_format) {
+    st->print("Index,Super");
+    for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+       if (selected[c]) {st->print(",%s", name_table[c]);}
+    }
+    st->print(",ClassName");
+  } else {
+    st->print("Index Super");
+    for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+      if (selected[c]) {st->print(str_fmt(width_table[c]), name_table[c]);}
+    }
+    st->print(" ClassName");
+  }
+
+  if (is_selected("ClassLoader")) {
+    st->print(",ClassLoader");
+  }
+  st->cr();
+}
+
+void KlassInfoHisto::print_class_stats(outputStream* st,
+                                      bool csv_format, const char *columns) {
+  ResourceMark rm;
+  KlassSizeStats sz, sz_sum;
+  int i;
+  julong *col_table = (julong*)(&sz);
+  julong *colsum_table = (julong*)(&sz_sum);
+  int width_table[KlassSizeStats::_num_columns];
+  bool selected[KlassSizeStats::_num_columns];
+
+  _selected_columns = columns;
+
+  memset(&sz_sum, 0, sizeof(sz_sum));
+  for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+    selected[c] = is_selected(name_table[c]);
+  }
+
+  for(i=0; i < elements()->length(); i++) {
+    elements()->at(i)->set_index(i+1);
+  }
+
+  for (int pass=1; pass<=2; pass++) {
+    if (pass == 2) {
+      print_title(st, csv_format, selected, width_table, name_table);
+    }
+    for(i=0; i < elements()->length(); i++) {
+      KlassInfoEntry* e = (KlassInfoEntry*)elements()->at(i);
+      const Klass* k = e->klass();
+
+      memset(&sz, 0, sizeof(sz));
+      sz._inst_count = e->count();
+      sz._inst_bytes = HeapWordSize * e->words();
+      k->collect_statistics(&sz);
+      sz._total_bytes = sz._ro_bytes + sz._rw_bytes;
+
+      if (pass == 1) {
+        for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+          colsum_table[c] += col_table[c];
+        }
+      } else {
+        int super_index = -1;
+        if (k->oop_is_instance()) {
+          Klass* super = ((InstanceKlass*)k)->java_super();
+          if (super) {
+            KlassInfoEntry* super_e = _cit->lookup(super);
+            if (super_e) {
+              super_index = super_e->index();
+            }
+          }
+        }
+
+        if (csv_format) {
+          st->print("%d,%d", e->index(), super_index);
+          for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+            if (selected[c]) {st->print("," JULONG_FORMAT, col_table[c]);}
+          }
+          st->print(",%s",e->name());
+        } else {
+          st->print("%5d %5d", e->index(), super_index);
+          for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+            if (selected[c]) {print_julong(st, width_table[c], col_table[c]);}
+          }
+          st->print(" %s", e->name());
+        }
+        if (is_selected("ClassLoader")) {
+          ClassLoaderData* loader_data = k->class_loader_data();
+          st->print(",");
+          loader_data->print_value_on(st);
+        }
+        st->cr();
+      }
+    }
+
+    if (pass == 1) {
+      for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+        width_table[c] = col_width(colsum_table[c], name_table[c]);
+      }
+    }
+  }
+
+  sz_sum._inst_size = 0;
+
+  if (csv_format) {
+    st->print(",");
+    for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+      if (selected[c]) {st->print("," JULONG_FORMAT, colsum_table[c]);}
+    }
+  } else {
+    st->print("           ");
+    for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+      if (selected[c]) {print_julong(st, width_table[c], colsum_table[c]);}
+    }
+    st->print(" Total");
+    if (sz_sum._total_bytes > 0) {
+      st->cr();
+      st->print("           ");
+      for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+        if (selected[c]) {
+          switch (c) {
+          case KlassSizeStats::_index_inst_size:
+          case KlassSizeStats::_index_inst_count:
+          case KlassSizeStats::_index_method_count:
+            st->print(str_fmt(width_table[c]), "-");
+            break;
+          default:
+            {
+              double perc = (double)(100) * (double)(colsum_table[c]) / (double)sz_sum._total_bytes;
+              st->print(perc_fmt(width_table[c]), perc);
+            }
+          }
+        }
+      }
+    }
+  }
+  st->cr();
+
+  if (!csv_format) {
+    print_title(st, csv_format, selected, width_table, name_table);
+  }
+}
+
+julong KlassInfoHisto::annotations_bytes(Array<AnnotationArray*>* p) const {
+  julong bytes = 0;
+  if (p != NULL) {
+    for (int i = 0; i < p->length(); i++) {
+      bytes += count_bytes_array(p->at(i));
+    }
+    bytes += count_bytes_array(p);
+  }
+  return bytes;
+}
+
+void KlassInfoHisto::print_histo_on(outputStream* st, bool print_stats,
+                                    bool csv_format, const char *columns) {
+  if (print_stats) {
+    print_class_stats(st, csv_format, columns);
+  } else {
+    st->print_cr("%s",title());
+    print_elements(st);
+  }
 }
 
 class HistoClosure : public KlassInfoClosure {
@@ -236,8 +463,26 @@
   CollectedHeap* heap = Universe::heap();
   bool is_shared_heap = false;
 
+  if (_print_help) {
+    for (int c=0; c<KlassSizeStats::_num_columns; c++) {
+      st->print("%s:\n\t", name_table[c]);
+      const int max_col = 60;
+      int col = 0;
+      for (const char *p = help_table[c]; *p; p++,col++) {
+        if (col >= max_col && *p == ' ') {
+          st->print("\n\t");
+          col = 0;
+        } else {
+          st->print("%c", *p);
+        }
+      }
+      st->print_cr(".\n");
+    }
+    return;
+  }
+
   // Collect klass instance info
-  KlassInfoTable cit(KlassInfoTable::cit_size, ref);
+  KlassInfoTable cit(KlassInfoTable::cit_size, ref, _print_class_stats);
   if (!cit.allocation_failed()) {
     // Iterate over objects in the heap
     RecordInstanceClosure ric(&cit);
@@ -252,14 +497,14 @@
                    missed_count);
     }
     // Sort and print klass instance info
-    KlassInfoHisto histo("\n"
-                     " num     #instances         #bytes  class name\n"
-                     "----------------------------------------------",
-                     KlassInfoHisto::histo_initial_size);
+    const char *title = "\n"
+              " num     #instances         #bytes  class name\n"
+              "----------------------------------------------";
+    KlassInfoHisto histo(&cit, title, KlassInfoHisto::histo_initial_size);
     HistoClosure hc(&histo);
     cit.iterate(&hc);
     histo.sort();
-    histo.print_on(st);
+    histo.print_histo_on(st, _print_class_stats, _csv_format, _columns);
   } else {
     st->print_cr("WARNING: Ran out of C-heap; histogram not generated");
   }
--- a/hotspot/src/share/vm/memory/heapInspection.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/memory/heapInspection.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -27,6 +27,7 @@
 
 #include "memory/allocation.inline.hpp"
 #include "oops/oop.inline.hpp"
+#include "oops/annotations.hpp"
 
 #if INCLUDE_SERVICES
 
@@ -44,16 +45,144 @@
 // to KlassInfoEntry's and is used to sort
 // the entries.
 
+#define HEAP_INSPECTION_COLUMNS_DO(f) \
+    f(inst_size, InstSize, \
+        "Size of each object instance of the Java class") \
+    f(inst_count, InstCount, \
+        "Number of object instances of the Java class")  \
+    f(inst_bytes, InstBytes, \
+        "This is usually (InstSize * InstNum). The only exception is " \
+        "java.lang.Class, whose InstBytes also includes the slots " \
+        "used to store static fields. InstBytes is not counted in " \
+        "ROAll, RWAll or Total") \
+    f(mirror_bytes, Mirror, \
+        "Size of the Klass::java_mirror() object") \
+    f(klass_bytes, KlassBytes, \
+        "Size of the InstanceKlass or ArrayKlass for this class. " \
+        "Note that this includes VTab, ITab, OopMap") \
+    f(secondary_supers_bytes, K_secondary_supers, \
+        "Number of bytes used by the Klass::secondary_supers() array") \
+    f(vtab_bytes, VTab, \
+        "Size of the embedded vtable in InstanceKlass") \
+    f(itab_bytes, ITab, \
+        "Size of the embedded itable in InstanceKlass") \
+    f(nonstatic_oopmap_bytes, OopMap, \
+        "Size of the embedded nonstatic_oop_map in InstanceKlass") \
+    f(methods_array_bytes, IK_methods, \
+        "Number of bytes used by the InstanceKlass::methods() array") \
+    f(method_ordering_bytes, IK_method_ordering, \
+        "Number of bytes used by the InstanceKlass::method_ordering() array") \
+    f(local_interfaces_bytes, IK_local_interfaces, \
+        "Number of bytes used by the InstanceKlass::local_interfaces() array") \
+    f(transitive_interfaces_bytes, IK_transitive_interfaces, \
+        "Number of bytes used by the InstanceKlass::transitive_interfaces() array") \
+    f(fields_bytes, IK_fields, \
+        "Number of bytes used by the InstanceKlass::fields() array") \
+    f(inner_classes_bytes, IK_inner_classes, \
+        "Number of bytes used by the InstanceKlass::inner_classes() array") \
+    f(signers_bytes, IK_signers, \
+        "Number of bytes used by the InstanceKlass::singers() array") \
+    f(class_annotations_bytes, class_annotations, \
+        "Size of class annotations") \
+    f(fields_annotations_bytes, fields_annotations, \
+        "Size of field annotations") \
+    f(methods_annotations_bytes, methods_annotations, \
+        "Size of method annotations") \
+    f(methods_parameter_annotations_bytes, methods_parameter_annotations, \
+        "Size of method parameter annotations") \
+    f(methods_default_annotations_bytes, methods_default_annotations, \
+        "Size of methods default annotations") \
+    f(type_annotations_bytes, type_annotations, \
+        "Size of type annotations") \
+    f(annotations_bytes, annotations, \
+        "Size of all annotations") \
+    f(cp_bytes, Cp, \
+        "Size of InstanceKlass::constants()") \
+    f(cp_tags_bytes, CpTags, \
+        "Size of InstanceKlass::constants()->tags()") \
+    f(cp_cache_bytes, CpCache, \
+        "Size of InstanceKlass::constants()->cache()") \
+    f(cp_operands_bytes, CpOperands, \
+        "Size of InstanceKlass::constants()->operands()") \
+    f(cp_refmap_bytes, CpRefMap, \
+        "Size of InstanceKlass::constants()->reference_map()") \
+    f(cp_all_bytes, CpAll, \
+        "Sum of Cp + CpTags + CpCache + CpOperands + CpRefMap") \
+    f(method_count, MethodCount, \
+        "Number of methods in this class") \
+    f(method_bytes, MethodBytes, \
+        "Size of the Method object") \
+    f(const_method_bytes, ConstMethod, \
+        "Size of the ConstMethod object") \
+    f(method_data_bytes, MethodData, \
+        "Size of the MethodData object") \
+    f(stackmap_bytes, StackMap, \
+        "Size of the stackmap_data") \
+    f(bytecode_bytes, Bytecodes, \
+        "Of the MethodBytes column, how much are the space taken up by bytecodes") \
+    f(method_all_bytes, MethodAll, \
+        "Sum of MethodBytes + Constmethod + Stackmap + Methoddata") \
+    f(ro_bytes, ROAll, \
+        "Size of all class meta data that could (potentially) be placed " \
+        "in read-only memory. (This could change with CDS design)") \
+    f(rw_bytes, RWAll, \
+        "Size of all class meta data that must be placed in read/write " \
+        "memory. (This could change with CDS design) ") \
+    f(total_bytes, Total, \
+        "ROAll + RWAll. Note that this does NOT include InstBytes.")
+
+// Size statistics for a Klass - filled in by Klass::collect_statistics()
+class KlassSizeStats {
+public:
+#define COUNT_KLASS_SIZE_STATS_FIELD(field, name, help)   _index_ ## field,
+#define DECLARE_KLASS_SIZE_STATS_FIELD(field, name, help) julong _ ## field;
+
+  enum {
+    HEAP_INSPECTION_COLUMNS_DO(COUNT_KLASS_SIZE_STATS_FIELD)
+    _num_columns
+  };
+
+  HEAP_INSPECTION_COLUMNS_DO(DECLARE_KLASS_SIZE_STATS_FIELD)
+
+  static int count(oop x) {
+    return (HeapWordSize * ((x) ? (x)->size() : 0));
+  }
+
+  static int count_array(objArrayOop x) {
+    return (HeapWordSize * ((x) ? (x)->size() : 0));
+  }
+
+  template <class T> static int count(T* x) {
+    return (HeapWordSize * ((x) ? (x)->size() : 0));
+  }
+
+  template <class T> static int count_array(T* x) {
+    if (x == NULL) {
+      return 0;
+    }
+    if (x->length() == 0) {
+      // This is a shared array, e.g., Universe::the_empty_int_array(). Don't
+      // count it to avoid double-counting.
+      return 0;
+    }
+    return HeapWordSize * x->size();
+  }
+};
+
+
+
+
 class KlassInfoEntry: public CHeapObj<mtInternal> {
  private:
   KlassInfoEntry* _next;
   Klass*          _klass;
   long            _instance_count;
   size_t          _instance_words;
+  long            _index;
 
  public:
   KlassInfoEntry(Klass* k, KlassInfoEntry* next) :
-    _klass(k), _instance_count(0), _instance_words(0), _next(next)
+    _klass(k), _instance_count(0), _instance_words(0), _next(next), _index(-1)
   {}
   KlassInfoEntry* next()     { return _next; }
   bool is_equal(Klass* k)  { return k == _klass; }
@@ -62,8 +191,11 @@
   void set_count(long ct)    { _instance_count = ct; }
   size_t words()             { return _instance_words; }
   void set_words(size_t wds) { _instance_words = wds; }
+  void set_index(long index) { _index = index; }
+  long index()               { return _index; }
   int compare(KlassInfoEntry* e1, KlassInfoEntry* e2);
   void print_on(outputStream* st) const;
+  const char* name() const;
 };
 
 class KlassInfoClosure: public StackObj {
@@ -95,45 +227,132 @@
 
   KlassInfoBucket* _buckets;
   uint hash(Klass* p);
-  KlassInfoEntry* lookup(Klass* const k);
+  KlassInfoEntry* lookup(Klass* const k); // allocates if not found!
+
+  class AllClassesFinder : public KlassClosure {
+    KlassInfoTable *_table;
+   public:
+    AllClassesFinder(KlassInfoTable* table) : _table(table) {}
+    virtual void do_klass(Klass* k);
+  };
 
  public:
   // Table size
   enum {
     cit_size = 20011
   };
-  KlassInfoTable(int size, HeapWord* ref);
+  KlassInfoTable(int size, HeapWord* ref, bool need_class_stats);
   ~KlassInfoTable();
   bool record_instance(const oop obj);
   void iterate(KlassInfoClosure* cic);
   bool allocation_failed() { return _buckets == NULL; }
+
+  friend class KlassInfoHisto;
 };
 
 class KlassInfoHisto : public StackObj {
  private:
+  KlassInfoTable *_cit;
   GrowableArray<KlassInfoEntry*>* _elements;
   GrowableArray<KlassInfoEntry*>* elements() const { return _elements; }
   const char* _title;
   const char* title() const { return _title; }
   static int sort_helper(KlassInfoEntry** e1, KlassInfoEntry** e2);
   void print_elements(outputStream* st) const;
+  void print_class_stats(outputStream* st, bool csv_format, const char *columns);
+  julong annotations_bytes(Array<AnnotationArray*>* p) const;
+  const char *_selected_columns;
+  bool is_selected(const char *col_name);
+  void print_title(outputStream* st, bool csv_format,
+                   bool selected_columns_table[], int width_table[],
+                   const char *name_table[]);
+
+  template <class T> static int count_bytes(T* x) {
+    return (HeapWordSize * ((x) ? (x)->size() : 0));
+  }
+
+  template <class T> static int count_bytes_array(T* x) {
+    if (x == NULL) {
+      return 0;
+    }
+    if (x->length() == 0) {
+      // This is a shared array, e.g., Universe::the_empty_int_array(). Don't
+      // count it to avoid double-counting.
+      return 0;
+    }
+    return HeapWordSize * x->size();
+  }
+
+  // returns a format string to print a julong with the given width. E.g,
+  // printf(num_fmt(6), julong(10)) would print out the number 10 with 4
+  // leading spaces.
+  static void print_julong(outputStream* st, int width, julong n) {
+    int num_spaces = width - julong_width(n);
+    if (num_spaces > 0) {
+      st->print(str_fmt(num_spaces), "");
+    }
+    st->print(JULONG_FORMAT, n);
+  }
+
+  static char* perc_fmt(int width) {
+    static char buf[32];
+    jio_snprintf(buf, sizeof(buf), "%%%d.1f%%%%", width-1);
+    return buf;
+  }
+
+  static char* str_fmt(int width) {
+    static char buf[32];
+    jio_snprintf(buf, sizeof(buf), "%%%ds", width);
+    return buf;
+  }
+
+  static int julong_width(julong n) {
+    if (n == 0) {
+      return 1;
+    }
+    int w = 0;
+    while (n > 0) {
+      n /= 10;
+      w += 1;
+    }
+    return w;
+  }
+
+  static int col_width(julong n, const char *name) {
+    int w = julong_width(n);
+    int min = (int)(strlen(name));
+    if (w < min) {
+        w = min;
+    }
+    // add a leading space for separation.
+    return w + 1;
+  }
+
  public:
   enum {
     histo_initial_size = 1000
   };
-  KlassInfoHisto(const char* title,
+  KlassInfoHisto(KlassInfoTable* cit, const char* title,
              int estimatedCount);
   ~KlassInfoHisto();
   void add(KlassInfoEntry* cie);
-  void print_on(outputStream* st) const;
+  void print_histo_on(outputStream* st, bool print_class_stats, bool csv_format, const char *columns);
   void sort();
 };
 
 #endif // INCLUDE_SERVICES
 
-class HeapInspection : public AllStatic {
+class HeapInspection : public StackObj {
+  bool _csv_format; // "comma separated values" format for spreadsheet.
+  bool _print_help;
+  bool _print_class_stats;
+  const char* _columns;
  public:
-  static void heap_inspection(outputStream* st, bool need_prologue) NOT_SERVICES_RETURN;
+  HeapInspection(bool csv_format, bool print_help,
+                 bool print_class_stats, const char *columns) :
+      _csv_format(csv_format), _print_help(print_help),
+      _print_class_stats(print_class_stats), _columns(columns) {}
+  void heap_inspection(outputStream* st, bool need_prologue) NOT_SERVICES_RETURN;
   static void find_instances_at_safepoint(Klass* k, GrowableArray<oop>* result) NOT_SERVICES_RETURN;
 };
 
--- a/hotspot/src/share/vm/oops/annotations.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/annotations.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "classfile/classLoaderData.hpp"
+#include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/oopFactory.hpp"
 #include "oops/annotations.hpp"
@@ -114,6 +115,50 @@
   st->print("Anotations(" INTPTR_FORMAT ")", this);
 }
 
+#if INCLUDE_SERVICES
+// Size Statistics
+
+julong Annotations::count_bytes(Array<AnnotationArray*>* p) {
+  julong bytes = 0;
+  if (p != NULL) {
+    for (int i = 0; i < p->length(); i++) {
+      bytes += KlassSizeStats::count_array(p->at(i));
+    }
+    bytes += KlassSizeStats::count_array(p);
+  }
+  return bytes;
+}
+
+void Annotations::collect_statistics(KlassSizeStats *sz) const {
+  sz->_annotations_bytes = sz->count(this);
+  sz->_class_annotations_bytes = sz->count(class_annotations());
+  sz->_fields_annotations_bytes = count_bytes(fields_annotations());
+  sz->_methods_annotations_bytes = count_bytes(methods_annotations());
+  sz->_methods_parameter_annotations_bytes =
+                          count_bytes(methods_parameter_annotations());
+  sz->_methods_default_annotations_bytes =
+                          count_bytes(methods_default_annotations());
+
+  const Annotations* type_anno = type_annotations();
+  if (type_anno != NULL) {
+    sz->_type_annotations_bytes = sz->count(type_anno);
+    sz->_type_annotations_bytes += sz->count(type_anno->class_annotations());
+    sz->_type_annotations_bytes += count_bytes(type_anno->fields_annotations());
+    sz->_type_annotations_bytes += count_bytes(type_anno->methods_annotations());
+  }
+
+  sz->_annotations_bytes +=
+      sz->_class_annotations_bytes +
+      sz->_fields_annotations_bytes +
+      sz->_methods_annotations_bytes +
+      sz->_methods_parameter_annotations_bytes +
+      sz->_methods_default_annotations_bytes +
+      sz->_type_annotations_bytes;
+
+  sz->_ro_bytes += sz->_annotations_bytes;
+}
+#endif // INCLUDE_SERVICES
+
 #define BULLET  " - "
 
 #ifndef PRODUCT
--- a/hotspot/src/share/vm/oops/annotations.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/annotations.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -34,6 +34,7 @@
 
 class ClassLoaderData;
 class outputStream;
+class KlassSizeStats;
 
 typedef Array<u1> AnnotationArray;
 
@@ -82,7 +83,12 @@
                                Array<AnnotationArray*>* mda, TRAPS);
   void deallocate_contents(ClassLoaderData* loader_data);
   DEBUG_ONLY(bool on_stack() { return false; })  // for template
+
+  // Sizing (in words)
   static int size()    { return sizeof(Annotations) / wordSize; }
+#if INCLUDE_SERVICES
+  void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   // Constructor to initialize to null
   Annotations() : _class_annotations(NULL),
@@ -142,7 +148,7 @@
   void set_methods_annotations_of(instanceKlassHandle ik,
                                   int idnum, AnnotationArray* anno,
                                   Array<AnnotationArray*>** md_p, TRAPS);
-
+  static julong count_bytes(Array<AnnotationArray*>* p);
  public:
   const char* internal_name() const { return "{constant pool}"; }
 #ifndef PRODUCT
--- a/hotspot/src/share/vm/oops/arrayKlass.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/arrayKlass.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -106,6 +106,14 @@
   static int header_size()                 { return sizeof(ArrayKlass)/HeapWordSize; }
   static int static_size(int header_size);
 
+#if INCLUDE_SERVICES
+  virtual void collect_statistics(KlassSizeStats *sz) const {
+    Klass::collect_statistics(sz);
+    // Do nothing for now, but remember to modify if you add new
+    // stuff to ArrayKlass.
+  }
+#endif
+
   // Java vtable
   klassVtable* vtable() const;             // return new klassVtable
   int  vtable_length() const               { return _vtable_len; }
--- a/hotspot/src/share/vm/oops/constMethod.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/constMethod.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "interpreter/interpreter.hpp"
 #include "memory/gcLocker.hpp"
+#include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "oops/constMethod.hpp"
 #include "oops/method.hpp"
@@ -330,6 +331,18 @@
   method()->print_value_on(st);
 }
 
+#if INCLUDE_SERVICES
+// Size Statistics
+void ConstMethod::collect_statistics(KlassSizeStats *sz) const {
+  int n1, n2, n3;
+  sz->_const_method_bytes += (n1 = sz->count(this));
+  sz->_bytecode_bytes     += (n2 = code_size());
+  sz->_stackmap_bytes     += (n3 = sz->count_array(stackmap_data()));
+
+  sz->_method_all_bytes += n1 + n3; // note: n2 is part of n3
+  sz->_ro_bytes += n1 + n3;
+}
+#endif // INCLUDE_SERVICES
 
 // Verification
 
--- a/hotspot/src/share/vm/oops/constMethod.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/constMethod.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -130,6 +130,7 @@
   u2 flags_lo;
 };
 
+class KlassSizeStats;
 
 class ConstMethod : public MetaspaceObj {
   friend class VMStructs;
@@ -320,6 +321,9 @@
 
   int size() const                    { return _constMethod_size;}
   void set_constMethod_size(int size)     { _constMethod_size = size; }
+#if INCLUDE_SERVICES
+  void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   // code size
   int code_size() const                          { return _code_size; }
--- a/hotspot/src/share/vm/oops/constantPool.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/constantPool.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -30,6 +30,7 @@
 #include "classfile/systemDictionary.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "interpreter/linkResolver.hpp"
+#include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/oopFactory.hpp"
 #include "oops/constantPool.hpp"
@@ -1946,6 +1947,20 @@
   }
 }
 
+#if INCLUDE_SERVICES
+// Size Statistics
+void ConstantPool::collect_statistics(KlassSizeStats *sz) const {
+  sz->_cp_all_bytes += (sz->_cp_bytes          = sz->count(this));
+  sz->_cp_all_bytes += (sz->_cp_tags_bytes     = sz->count_array(tags()));
+  sz->_cp_all_bytes += (sz->_cp_cache_bytes    = sz->count(cache()));
+  sz->_cp_all_bytes += (sz->_cp_operands_bytes = sz->count_array(operands()));
+  sz->_cp_all_bytes += (sz->_cp_refmap_bytes   = sz->count_array(reference_map()));
+
+  sz->_ro_bytes += sz->_cp_operands_bytes + sz->_cp_tags_bytes +
+                   sz->_cp_refmap_bytes;
+  sz->_rw_bytes += sz->_cp_bytes + sz->_cp_cache_bytes;
+}
+#endif // INCLUDE_SERVICES
 
 // Verification
 
--- a/hotspot/src/share/vm/oops/constantPool.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/constantPool.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -80,6 +80,7 @@
   }
 };
 
+class KlassSizeStats;
 class ConstantPool : public Metadata {
   friend class VMStructs;
   friend class BytecodeInterpreter;  // Directly extracts an oop in the pool for fast instanceof/checkcast
@@ -684,9 +685,13 @@
     return 0 <= index && index < length();
   }
 
+  // Sizing (in words)
   static int header_size()             { return sizeof(ConstantPool)/HeapWordSize; }
   static int size(int length)          { return align_object_size(header_size() + length); }
   int size() const                     { return size(length()); }
+#if INCLUDE_SERVICES
+  void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   friend class ClassFileParser;
   friend class SystemDictionary;
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -34,6 +34,7 @@
 #include "interpreter/rewriter.hpp"
 #include "jvmtifiles/jvmti.h"
 #include "memory/genOopClosures.inline.hpp"
+#include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/oopFactory.hpp"
 #include "oops/fieldStreams.hpp"
@@ -2960,6 +2961,52 @@
   return external_name();
 }
 
+#if INCLUDE_SERVICES
+// Size Statistics
+void InstanceKlass::collect_statistics(KlassSizeStats *sz) const {
+  Klass::collect_statistics(sz);
+
+  sz->_inst_size  = HeapWordSize * size_helper();
+  sz->_vtab_bytes = HeapWordSize * align_object_offset(vtable_length());
+  sz->_itab_bytes = HeapWordSize * align_object_offset(itable_length());
+  sz->_nonstatic_oopmap_bytes = HeapWordSize *
+        ((is_interface() || is_anonymous()) ?
+         align_object_offset(nonstatic_oop_map_size()) :
+         nonstatic_oop_map_size());
+
+  int n = 0;
+  n += (sz->_methods_array_bytes         = sz->count_array(methods()));
+  n += (sz->_method_ordering_bytes       = sz->count_array(method_ordering()));
+  n += (sz->_local_interfaces_bytes      = sz->count_array(local_interfaces()));
+  n += (sz->_transitive_interfaces_bytes = sz->count_array(transitive_interfaces()));
+  n += (sz->_signers_bytes               = sz->count_array(signers()));
+  n += (sz->_fields_bytes                = sz->count_array(fields()));
+  n += (sz->_inner_classes_bytes         = sz->count_array(inner_classes()));
+  sz->_ro_bytes += n;
+
+  const ConstantPool* cp = constants();
+  if (cp) {
+    cp->collect_statistics(sz);
+  }
+
+  const Annotations* anno = annotations();
+  if (anno) {
+    anno->collect_statistics(sz);
+  }
+
+  const Array<Method*>* methods_array = methods();
+  if (methods()) {
+    for (int i = 0; i < methods_array->length(); i++) {
+      Method* method = methods_array->at(i);
+      if (method) {
+        sz->_method_count ++;
+        method->collect_statistics(sz);
+      }
+    }
+  }
+}
+#endif // INCLUDE_SERVICES
+
 // Verification
 
 class VerifyFieldClosure: public OopClosure {
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -826,6 +826,9 @@
                                                is_interface(),
                                                is_anonymous());
   }
+#if INCLUDE_SERVICES
+  virtual void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   static int vtable_start_offset()    { return header_size(); }
   static int vtable_length_offset()   { return offset_of(InstanceKlass, _vtable_len) / HeapWordSize; }
--- a/hotspot/src/share/vm/oops/klass.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/klass.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -29,6 +29,7 @@
 #include "classfile/vmSymbols.hpp"
 #include "gc_implementation/shared/markSweep.inline.hpp"
 #include "gc_interface/collectedHeap.inline.hpp"
+#include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/oopFactory.hpp"
 #include "memory/resourceArea.hpp"
@@ -624,6 +625,17 @@
   obj->print_address_on(st);
 }
 
+#if INCLUDE_SERVICES
+// Size Statistics
+void Klass::collect_statistics(KlassSizeStats *sz) const {
+  sz->_klass_bytes = sz->count(this);
+  sz->_mirror_bytes = sz->count(java_mirror());
+  sz->_secondary_supers_bytes = sz->count_array(secondary_supers());
+
+  sz->_ro_bytes += sz->_secondary_supers_bytes;
+  sz->_rw_bytes += sz->_klass_bytes + sz->_mirror_bytes;
+}
+#endif // INCLUDE_SERVICES
 
 // Verification
 
--- a/hotspot/src/share/vm/oops/klass.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/klass.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -91,6 +91,7 @@
 class ClassLoaderData;
 class klassVtable;
 class ParCompactionManager;
+class KlassSizeStats;
 
 class Klass : public Metadata {
   friend class VMStructs;
@@ -477,6 +478,9 @@
 
   // Size of klass in word size.
   virtual int size() const = 0;
+#if INCLUDE_SERVICES
+  virtual void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   // Returns the Java name for a class (Resource allocated)
   // For arrays, this returns the name of the element with a leading '['.
--- a/hotspot/src/share/vm/oops/method.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/method.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -34,6 +34,7 @@
 #include "interpreter/oopMapCache.hpp"
 #include "memory/gcLocker.hpp"
 #include "memory/generation.hpp"
+#include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/oopFactory.hpp"
 #include "oops/constMethod.hpp"
@@ -1954,6 +1955,22 @@
   if (WizardMode && code() != NULL) st->print(" ((nmethod*)%p)", code());
 }
 
+#if INCLUDE_SERVICES
+// Size Statistics
+void Method::collect_statistics(KlassSizeStats *sz) const {
+  int mysize = sz->count(this);
+  sz->_method_bytes += mysize;
+  sz->_method_all_bytes += mysize;
+  sz->_rw_bytes += mysize;
+
+  if (constMethod()) {
+    constMethod()->collect_statistics(sz);
+  }
+  if (method_data()) {
+    method_data()->collect_statistics(sz);
+  }
+}
+#endif // INCLUDE_SERVICES
 
 // Verification
 
--- a/hotspot/src/share/vm/oops/method.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/method.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -101,6 +101,7 @@
 class AdapterHandlerEntry;
 class MethodData;
 class ConstMethod;
+class KlassSizeStats;
 
 class Method : public Metadata {
  friend class VMStructs;
@@ -593,6 +594,9 @@
   static int header_size()                       { return sizeof(Method)/HeapWordSize; }
   static int size(bool is_native);
   int size() const                               { return method_size(); }
+#if INCLUDE_SERVICES
+  void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   // interpreter support
   static ByteSize const_offset()                 { return byte_offset_of(Method, _constMethod       ); }
--- a/hotspot/src/share/vm/oops/methodData.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/methodData.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -27,6 +27,7 @@
 #include "interpreter/bytecode.hpp"
 #include "interpreter/bytecodeStream.hpp"
 #include "interpreter/linkResolver.hpp"
+#include "memory/heapInspection.hpp"
 #include "oops/methodData.hpp"
 #include "prims/jvmtiRedefineClasses.hpp"
 #include "runtime/compilationPolicy.hpp"
@@ -859,6 +860,15 @@
 }
 #endif
 
+#if INCLUDE_SERVICES
+// Size Statistics
+void MethodData::collect_statistics(KlassSizeStats *sz) const {
+  int n = sz->count(this);
+  sz->_method_data_bytes += n;
+  sz->_method_all_bytes += n;
+  sz->_rw_bytes += n;
+}
+#endif // INCLUDE_SERVICES
 
 // Verification
 
--- a/hotspot/src/share/vm/oops/methodData.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/oops/methodData.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -32,6 +32,7 @@
 #include "runtime/orderAccess.hpp"
 
 class BytecodeStream;
+class KlassSizeStats;
 
 // The MethodData object collects counts and other profile information
 // during zeroth-tier (interpretive) and first-tier execution.
@@ -1289,6 +1290,9 @@
   // My size
   int size_in_bytes() const { return _size; }
   int size() const    { return align_object_size(align_size_up(_size, BytesPerWord)/BytesPerWord); }
+#if INCLUDE_SERVICES
+  void collect_statistics(KlassSizeStats *sz) const;
+#endif
 
   int      creation_mileage() const  { return _creation_mileage; }
   void set_creation_mileage(int x)   { _creation_mileage = x; }
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -43,12 +43,12 @@
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<VMUptimeDCmd>(true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SystemGCDCmd>(true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RunFinalizationDCmd>(true, false));
-#if INCLUDE_SERVICES // Heap dumping supported
+#if INCLUDE_SERVICES // Heap dumping/inspection supported
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(true, false));
 #endif // INCLUDE_SERVICES
-  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(true, false));
-
   //Enhanced JMX Agent Support
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartRemoteDCmd>(true,false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JMXStartLocalDCmd>(true,false));
@@ -252,7 +252,7 @@
                          vmSymbols::void_method_signature(), CHECK);
 }
 
-#if INCLUDE_SERVICES // Heap dumping supported
+#if INCLUDE_SERVICES // Heap dumping/inspection supported
 HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) :
                            DCmdWithParser(output, heap),
   _filename("filename","Name of the dump file", "STRING",true),
@@ -292,7 +292,6 @@
     return 0;
   }
 }
-#endif // INCLUDE_SERVICES
 
 ClassHistogramDCmd::ClassHistogramDCmd(outputStream* output, bool heap) :
                                        DCmdWithParser(output, heap),
@@ -319,6 +318,65 @@
   }
 }
 
+#define DEFAULT_COLUMNS "InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total"
+ClassStatsDCmd::ClassStatsDCmd(outputStream* output, bool heap) :
+                                       DCmdWithParser(output, heap),
+  _csv("-csv", "Print in CSV (comma-separated values) format for spreadsheets",
+       "BOOLEAN", false, "false"),
+  _all("-all", "Show all columns",
+       "BOOLEAN", false, "false"),
+  _help("-help", "Show meaning of all the columns",
+       "BOOLEAN", false, "false"),
+  _columns("columns", "Comma-separated list of all the columns to show. "
+           "If not specified, the following columns are shown: " DEFAULT_COLUMNS,
+           "STRING", false) {
+  _dcmdparser.add_dcmd_option(&_all);
+  _dcmdparser.add_dcmd_option(&_csv);
+  _dcmdparser.add_dcmd_option(&_help);
+  _dcmdparser.add_dcmd_argument(&_columns);
+}
+
+void ClassStatsDCmd::execute(TRAPS) {
+  if (!UnlockDiagnosticVMOptions) {
+    output()->print_cr("GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions");
+    return;
+  }
+
+  VM_GC_HeapInspection heapop(output(),
+                              true, /* request_full_gc */
+                              true /* need_prologue */);
+  heapop.set_csv_format(_csv.value());
+  heapop.set_print_help(_help.value());
+  heapop.set_print_class_stats(true);
+  if (_all.value()) {
+    if (_columns.has_value()) {
+      output()->print_cr("Cannot specify -all and individual columns at the same time");
+      return;
+    } else {
+      heapop.set_columns(NULL);
+    }
+  } else {
+    if (_columns.has_value()) {
+      heapop.set_columns(_columns.value());
+    } else {
+      heapop.set_columns(DEFAULT_COLUMNS);
+    }
+  }
+  VMThread::execute(&heapop);
+}
+
+int ClassStatsDCmd::num_arguments() {
+  ResourceMark rm;
+  ClassStatsDCmd* dcmd = new ClassStatsDCmd(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+#endif // INCLUDE_SERVICES
+
 ThreadDumpDCmd::ThreadDumpDCmd(outputStream* output, bool heap) :
                                DCmdWithParser(output, heap),
   _locks("-l", "print java.util.concurrent locks", "BOOLEAN", false, "false") {
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp	Thu Jan 24 23:30:45 2013 -0800
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp	Fri Jan 25 15:06:18 2013 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -178,7 +178,7 @@
 };
 #endif // INCLUDE_SERVICES
 
-// See also: inspeactheap in attachListener.cpp
+// See also: inspectheap in attachListener.cpp
 class ClassHistogramDCmd : public DCmdWithParser {
 protected:
   DCmdArgument<bool> _all;
@@ -197,6 +197,27 @@
   virtual void execute(TRAPS);
 };
 
+class ClassStatsDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<bool> _all;
+  DCmdArgument<bool> _csv;
+  DCmdArgument<bool> _help;
+  DCmdArgument<char*> _columns;
+public:
+  ClassStatsDCmd(outputStream* output, bool heap);
+  static const char* name() {
+    return "GC.class_stats";
+  }
+  static const char* description() {
+    return "Provide statistics about Java class meta data. Requires -XX:+UnlockDiagnosticVMOptions.";
+  }
+  static const char* impact() {
+    return "High: Depends on Java heap size and content.";
+  }
+  static int num_arguments();
+  virtual void execute(TRAPS);
+};
+
 // See also: thread_dump in attachListener.cpp
 class ThreadDumpDCmd : public DCmdWithParser {
 protected: