8014262: PrintStringTableStatistics should include more footprint info
authoriklam
Sat, 18 May 2013 20:41:01 -0700
changeset 17610 c6857feaac47
parent 17609 c3a139744233
child 17611 5b5a5ad4c222
8014262: PrintStringTableStatistics should include more footprint info Summary: Added info for the string/symbol objects and the hash entries Reviewed-by: coleenp, rbackman
hotspot/src/share/vm/classfile/symbolTable.cpp
hotspot/src/share/vm/utilities/hashtable.cpp
hotspot/src/share/vm/utilities/hashtable.hpp
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp	Fri May 17 17:52:07 2013 -0700
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp	Sat May 18 20:41:01 2013 -0700
@@ -35,7 +35,6 @@
 #include "oops/oop.inline2.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "utilities/hashtable.inline.hpp"
-#include "utilities/numberSeq.hpp"
 
 // --------------------------------------------------------------------------
 
@@ -451,21 +450,7 @@
 }
 
 void SymbolTable::dump(outputStream* st) {
-  NumberSeq summary;
-  for (int i = 0; i < the_table()->table_size(); ++i) {
-    int count = 0;
-    for (HashtableEntry<Symbol*, mtSymbol>* e = the_table()->bucket(i);
-       e != NULL; e = e->next()) {
-      count++;
-    }
-    summary.add((double)count);
-  }
-  st->print_cr("SymbolTable statistics:");
-  st->print_cr("Number of buckets       : %7d", summary.num());
-  st->print_cr("Average bucket size     : %7.0f", summary.avg());
-  st->print_cr("Variance of bucket size : %7.0f", summary.variance());
-  st->print_cr("Std. dev. of bucket size: %7.0f", summary.sd());
-  st->print_cr("Maximum bucket size     : %7.0f", summary.maximum());
+  the_table()->dump_table(st, "SymbolTable");
 }
 
 
@@ -814,21 +799,7 @@
 }
 
 void StringTable::dump(outputStream* st) {
-  NumberSeq summary;
-  for (int i = 0; i < the_table()->table_size(); ++i) {
-    HashtableEntry<oop, mtSymbol>* p = the_table()->bucket(i);
-    int count = 0;
-    for ( ; p != NULL; p = p->next()) {
-      count++;
-    }
-    summary.add((double)count);
-  }
-  st->print_cr("StringTable statistics:");
-  st->print_cr("Number of buckets       : %7d", summary.num());
-  st->print_cr("Average bucket size     : %7.0f", summary.avg());
-  st->print_cr("Variance of bucket size : %7.0f", summary.variance());
-  st->print_cr("Std. dev. of bucket size: %7.0f", summary.sd());
-  st->print_cr("Maximum bucket size     : %7.0f", summary.maximum());
+  the_table()->dump_table(st, "StringTable");
 }
 
 
--- a/hotspot/src/share/vm/utilities/hashtable.cpp	Fri May 17 17:52:07 2013 -0700
+++ b/hotspot/src/share/vm/utilities/hashtable.cpp	Sat May 18 20:41:01 2013 -0700
@@ -33,6 +33,7 @@
 #include "utilities/dtrace.hpp"
 #include "utilities/hashtable.hpp"
 #include "utilities/hashtable.inline.hpp"
+#include "utilities/numberSeq.hpp"
 
 
 // This is a generic hashtable, designed to be used for the symbol
@@ -237,6 +238,57 @@
   }
 }
 
+template <class T, MEMFLAGS F> int Hashtable<T, F>::literal_size(Symbol *symbol) {
+  return symbol->size() * HeapWordSize;
+}
+
+template <class T, MEMFLAGS F> int Hashtable<T, F>::literal_size(oop oop) {
+  // NOTE: this would over-count if (pre-JDK8) java_lang_Class::has_offset_field() is true,
+  // and the String.value array is shared by several Strings. However, starting from JDK8,
+  // the String.value array is not shared anymore.
+  assert(oop != NULL && oop->klass() == SystemDictionary::String_klass(), "only strings are supported");
+  return (oop->size() + java_lang_String::value(oop)->size()) * HeapWordSize;
+}
+
+// Dump footprint and bucket length statistics
+//
+// Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
+// add a new function Hashtable<T, F>::literal_size(MyNewType lit)
+
+template <class T, MEMFLAGS F> void Hashtable<T, F>::dump_table(outputStream* st, const char *table_name) {
+  NumberSeq summary;
+  int literal_bytes = 0;
+  for (int i = 0; i < this->table_size(); ++i) {
+    int count = 0;
+    for (HashtableEntry<T, F>* e = bucket(i);
+       e != NULL; e = e->next()) {
+      count++;
+      literal_bytes += literal_size(e->literal());
+    }
+    summary.add((double)count);
+  }
+  double num_buckets = summary.num();
+  double num_entries = summary.sum();
+
+  int bucket_bytes = (int)num_buckets * sizeof(bucket(0));
+  int entry_bytes  = (int)num_entries * sizeof(HashtableEntry<T, F>);
+  int total_bytes = literal_bytes +  bucket_bytes + entry_bytes;
+
+  double bucket_avg  = (num_buckets <= 0) ? 0 : (bucket_bytes  / num_buckets);
+  double entry_avg   = (num_entries <= 0) ? 0 : (entry_bytes   / num_entries);
+  double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries);
+
+  st->print_cr("%s statistics:", table_name);
+  st->print_cr("Number of buckets       : %9d = %9d bytes, avg %7.3f", (int)num_buckets, bucket_bytes,  bucket_avg);
+  st->print_cr("Number of entries       : %9d = %9d bytes, avg %7.3f", (int)num_entries, entry_bytes,   entry_avg);
+  st->print_cr("Number of literals      : %9d = %9d bytes, avg %7.3f", (int)num_entries, literal_bytes, literal_avg);
+  st->print_cr("Total footprint         : %9s = %9d bytes", "", total_bytes);
+  st->print_cr("Average bucket size     : %9.3f", summary.avg());
+  st->print_cr("Variance of bucket size : %9.3f", summary.variance());
+  st->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
+  st->print_cr("Maximum bucket size     : %9d", (int)summary.maximum());
+}
+
 
 // Dump the hash table buckets.
 
--- a/hotspot/src/share/vm/utilities/hashtable.hpp	Fri May 17 17:52:07 2013 -0700
+++ b/hotspot/src/share/vm/utilities/hashtable.hpp	Sat May 18 20:41:01 2013 -0700
@@ -282,6 +282,19 @@
   static bool use_alternate_hashcode()  { return _seed != 0; }
   static jint seed()                    { return _seed; }
 
+  static int literal_size(Symbol *symbol);
+  static int literal_size(oop oop);
+
+  // The following two are currently not used, but are needed anyway because some
+  // C++ compilers (MacOS and Solaris) force the instantiation of
+  // Hashtable<ConstantPool*, mtClass>::dump_table() even though we never call this function
+  // in the VM code.
+  static int literal_size(ConstantPool *cp) {Unimplemented(); return 0;}
+  static int literal_size(Klass *k)         {Unimplemented(); return 0;}
+
+public:
+  void dump_table(outputStream* st, const char *table_name);
+
  private:
   static jint _seed;
 };