hotspot/src/share/vm/classfile/symbolTable.cpp
changeset 8076 96d498ec7ae1
parent 7397 5b173b4ca846
child 8655 fec854507832
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp	Thu Jan 27 13:42:28 2011 -0800
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp	Thu Jan 27 16:11:27 2011 -0800
@@ -31,7 +31,6 @@
 #include "memory/gcLocker.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/oop.inline2.hpp"
-#include "oops/symbolKlass.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "utilities/hashtable.inline.hpp"
 
@@ -39,14 +38,97 @@
 
 SymbolTable* SymbolTable::_the_table = NULL;
 
+Symbol* SymbolTable::allocate_symbol(const u1* name, int len, TRAPS) {
+  // Don't allow symbols to be created which cannot fit in a Symbol*.
+  if (len > Symbol::max_length()) {
+    THROW_MSG_0(vmSymbols::java_lang_InternalError(),
+                "name is too long to represent");
+  }
+  Symbol* sym = new (len) Symbol(name, len);
+  assert(sym != NULL, "new should call vm_exit_out_of_memory if C_HEAP is exhausted");
+  return sym;
+}
+
+bool SymbolTable::allocate_symbols(int names_count, const u1** names,
+                                   int* lengths, Symbol** syms, TRAPS) {
+  for (int i = 0; i< names_count; i++) {
+    if (lengths[i] > Symbol::max_length()) {
+      THROW_MSG_0(vmSymbols::java_lang_InternalError(),
+                  "name is too long to represent");
+    }
+  }
+
+  for (int i = 0; i< names_count; i++) {
+    int len = lengths[i];
+    syms[i] = new (len) Symbol(names[i], len);
+    assert(syms[i] != NULL, "new should call vm_exit_out_of_memory if "
+                            "C_HEAP is exhausted");
+  }
+  return true;
+}
+
+// Call function for all symbols in the symbol table.
+void SymbolTable::symbols_do(SymbolClosure *cl) {
+  const int n = the_table()->table_size();
+  for (int i = 0; i < n; i++) {
+    for (HashtableEntry<Symbol*>* p = the_table()->bucket(i);
+         p != NULL;
+         p = p->next()) {
+      cl->do_symbol(p->literal_addr());
+    }
+  }
+}
+
+int SymbolTable::symbols_removed = 0;
+int SymbolTable::symbols_counted = 0;
+
+// Remove unreferenced symbols from the symbol table
+// This is done late during GC.  This doesn't use the hash table unlink because
+// it assumes that the literals are oops.
+void SymbolTable::unlink() {
+  int removed = 0;
+  int total = 0;
+  int memory_total = 0;
+  for (int i = 0; i < the_table()->table_size(); ++i) {
+    for (HashtableEntry<Symbol*>** p = the_table()->bucket_addr(i); *p != NULL; ) {
+      HashtableEntry<Symbol*>* entry = *p;
+      if (entry->is_shared()) {
+        break;
+      }
+      Symbol* s = entry->literal();
+      memory_total += s->object_size();
+      total++;
+      assert(s != NULL, "just checking");
+      // If reference count is zero, remove.
+      if (s->refcount() == 0) {
+        delete s;
+        removed++;
+        *p = entry->next();
+        the_table()->free_entry(entry);
+      } else {
+        p = entry->next_addr();
+      }
+    }
+  }
+  symbols_removed += removed;
+  symbols_counted += total;
+  if (PrintGCDetails) {
+    gclog_or_tty->print(" [Symbols=%d size=%dK] ", total,
+                        (memory_total*HeapWordSize)/1024);
+  }
+}
+
+
 // Lookup a symbol in a bucket.
 
-symbolOop SymbolTable::lookup(int index, const char* name,
+Symbol* SymbolTable::lookup(int index, const char* name,
                               int len, unsigned int hash) {
-  for (HashtableEntry* e = bucket(index); e != NULL; e = e->next()) {
+  for (HashtableEntry<Symbol*>* e = bucket(index); e != NULL; e = e->next()) {
     if (e->hash() == hash) {
-      symbolOop sym = symbolOop(e->literal());
+      Symbol* sym = e->literal();
       if (sym->equals(name, len)) {
+        // something is referencing this symbol now.
+        sym->increment_refcount();
         return sym;
       }
     }
@@ -62,11 +144,11 @@
 // entries in the symbol table during normal execution (only during
 // safepoints).
 
-symbolOop SymbolTable::lookup(const char* name, int len, TRAPS) {
+Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
   unsigned int hashValue = hash_symbol(name, len);
   int index = the_table()->hash_to_index(hashValue);
 
-  symbolOop s = the_table()->lookup(index, name, len, hashValue);
+  Symbol* s = the_table()->lookup(index, name, len, hashValue);
 
   // Found
   if (s != NULL) return s;
@@ -75,7 +157,7 @@
   return the_table()->basic_add(index, (u1*)name, len, hashValue, CHECK_NULL);
 }
 
-symbolOop SymbolTable::lookup(symbolHandle sym, int begin, int end, TRAPS) {
+Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) {
   char* buffer;
   int index, len;
   unsigned int hashValue;
@@ -87,7 +169,7 @@
     len = end - begin;
     hashValue = hash_symbol(name, len);
     index = the_table()->hash_to_index(hashValue);
-    symbolOop s = the_table()->lookup(index, name, len, hashValue);
+    Symbol* s = the_table()->lookup(index, name, len, hashValue);
 
     // Found
     if (s != NULL) return s;
@@ -111,18 +193,19 @@
   return the_table()->basic_add(index, (u1*)buffer, len, hashValue, CHECK_NULL);
 }
 
-symbolOop SymbolTable::lookup_only(const char* name, int len,
+Symbol* SymbolTable::lookup_only(const char* name, int len,
                                    unsigned int& hash) {
   hash = hash_symbol(name, len);
   int index = the_table()->hash_to_index(hash);
 
-  return the_table()->lookup(index, name, len, hash);
+  Symbol* s = the_table()->lookup(index, name, len, hash);
+  return s;
 }
 
 // Suggestion: Push unicode-based lookup all the way into the hashing
 // and probing logic, so there is no need for convert_to_utf8 until
-// an actual new symbolOop is created.
-symbolOop SymbolTable::lookup_unicode(const jchar* name, int utf16_length, TRAPS) {
+// an actual new Symbol* is created.
+Symbol* SymbolTable::lookup_unicode(const jchar* name, int utf16_length, TRAPS) {
   int utf8_length = UNICODE::utf8_length((jchar*) name, utf16_length);
   char stack_buf[128];
   if (utf8_length < (int) sizeof(stack_buf)) {
@@ -137,7 +220,7 @@
   }
 }
 
-symbolOop SymbolTable::lookup_only_unicode(const jchar* name, int utf16_length,
+Symbol* SymbolTable::lookup_only_unicode(const jchar* name, int utf16_length,
                                            unsigned int& hash) {
   int utf8_length = UNICODE::utf8_length((jchar*) name, utf16_length);
   char stack_buf[128];
@@ -163,25 +246,23 @@
     // do it the hard way
     for (int i=0; i<names_count; i++) {
       int index = table->hash_to_index(hashValues[i]);
-      symbolOop sym = table->basic_add(index, (u1*)names[i], lengths[i],
+      Symbol* sym = table->basic_add(index, (u1*)names[i], lengths[i],
                                        hashValues[i], CHECK);
       cp->symbol_at_put(cp_indices[i], sym);
     }
   }
 }
 
-symbolOop SymbolTable::basic_add(int index, u1 *name, int len,
+Symbol* SymbolTable::basic_add(int index, u1 *name, int len,
                                  unsigned int hashValue, TRAPS) {
   assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(),
          "proposed name of symbol must be stable");
 
   // We assume that lookup() has been called already, that it failed,
   // and symbol was not found.  We create the symbol here.
-  symbolKlass* sk  = (symbolKlass*) Universe::symbolKlassObj()->klass_part();
-  symbolOop s_oop = sk->allocate_symbol(name, len, CHECK_NULL);
-  symbolHandle sym (THREAD, s_oop);
+  Symbol* sym = allocate_symbol(name, len, CHECK_NULL);
 
-  // Allocation must be done before grapping the SymbolTable_lock lock
+  // Allocation must be done before grabbing the SymbolTable_lock lock
   MutexLocker ml(SymbolTable_lock, THREAD);
 
   assert(sym->equals((char*)name, len), "symbol must be properly initialized");
@@ -189,51 +270,51 @@
   // Since look-up was done lock-free, we need to check if another
   // thread beat us in the race to insert the symbol.
 
-  symbolOop test = lookup(index, (char*)name, len, hashValue);
+  Symbol* test = lookup(index, (char*)name, len, hashValue);
   if (test != NULL) {
     // A race occurred and another thread introduced the symbol, this one
     // will be dropped and collected.
+    delete sym;
+    assert(test->refcount() != 0, "lookup should have incremented the count");
     return test;
   }
 
-  HashtableEntry* entry = new_entry(hashValue, sym());
+  HashtableEntry<Symbol*>* entry = new_entry(hashValue, sym);
+  sym->increment_refcount();
   add_entry(index, entry);
-  return sym();
+  return sym;
 }
 
 bool SymbolTable::basic_add(constantPoolHandle cp, int names_count,
                             const char** names, int* lengths,
                             int* cp_indices, unsigned int* hashValues,
                             TRAPS) {
-  symbolKlass* sk  = (symbolKlass*) Universe::symbolKlassObj()->klass_part();
-  symbolOop sym_oops[symbol_alloc_batch_size];
-  bool allocated = sk->allocate_symbols(names_count, names, lengths,
-                                        sym_oops, CHECK_false);
+  Symbol* syms[symbol_alloc_batch_size];
+  bool allocated = allocate_symbols(names_count, (const u1**)names, lengths,
+                                    syms, CHECK_false);
   if (!allocated) {
     return false;
   }
-  symbolHandle syms[symbol_alloc_batch_size];
-  int i;
-  for (i=0; i<names_count; i++) {
-    syms[i] = symbolHandle(THREAD, sym_oops[i]);
-  }
 
   // Allocation must be done before grabbing the SymbolTable_lock lock
   MutexLocker ml(SymbolTable_lock, THREAD);
 
-  for (i=0; i<names_count; i++) {
+  for (int i=0; i<names_count; i++) {
     assert(syms[i]->equals(names[i], lengths[i]), "symbol must be properly initialized");
     // Since look-up was done lock-free, we need to check if another
     // thread beat us in the race to insert the symbol.
     int index = hash_to_index(hashValues[i]);
-    symbolOop test = lookup(index, names[i], lengths[i], hashValues[i]);
+    Symbol* test = lookup(index, names[i], lengths[i], hashValues[i]);
     if (test != NULL) {
       // A race occurred and another thread introduced the symbol, this one
       // will be dropped and collected. Use test instead.
       cp->symbol_at_put(cp_indices[i], test);
+      assert(test->refcount() != 0, "lookup should have incremented the count");
+      delete syms[i];
     } else {
-      symbolOop sym = syms[i]();
-      HashtableEntry* entry = new_entry(hashValues[i], sym);
+      Symbol* sym = syms[i];
+      HashtableEntry<Symbol*>* entry = new_entry(hashValues[i], sym);
+      sym->increment_refcount();  // increment refcount in external hashtable
       add_entry(index, entry);
       cp->symbol_at_put(cp_indices[i], sym);
     }
@@ -245,12 +326,10 @@
 
 void SymbolTable::verify() {
   for (int i = 0; i < the_table()->table_size(); ++i) {
-    HashtableEntry* p = the_table()->bucket(i);
+    HashtableEntry<Symbol*>* p = the_table()->bucket(i);
     for ( ; p != NULL; p = p->next()) {
-      symbolOop s = symbolOop(p->literal());
+      Symbol* s = (Symbol*)(p->literal());
       guarantee(s != NULL, "symbol is NULL");
-      s->verify();
-      guarantee(s->is_perm(), "symbol not in permspace");
       unsigned int h = hash_symbol((char*)s->bytes(), s->utf8_length());
       guarantee(p->hash() == h, "broken hash in symbol table entry");
       guarantee(the_table()->hash_to_index(h) == i,
@@ -279,10 +358,14 @@
   int total = 0;
   int max_symbols = 0;
   int out_of_range = 0;
+  int memory_total = 0;
+  int count = 0;
   for (i = 0; i < the_table()->table_size(); i++) {
-    HashtableEntry* p = the_table()->bucket(i);
+    HashtableEntry<Symbol*>* p = the_table()->bucket(i);
     for ( ; p != NULL; p = p->next()) {
-      int counter = symbolOop(p->literal())->utf8_length();
+      memory_total += p->literal()->object_size();
+      count++;
+      int counter = p->literal()->utf8_length();
       total += counter;
       if (counter < results_length) {
         results[counter]++;
@@ -293,6 +376,17 @@
     }
   }
   tty->print_cr("Symbol Table:");
+  tty->print_cr("Total number of symbols  %5d", count);
+  tty->print_cr("Total size in memory     %5dK",
+          (memory_total*HeapWordSize)/1024);
+  tty->print_cr("Total counted            %5d", symbols_counted);
+  tty->print_cr("Total removed            %5d", symbols_removed);
+  if (symbols_counted > 0) {
+    tty->print_cr("Percent removed          %3.2f",
+          ((float)symbols_removed/(float)symbols_counted)* 100);
+  }
+  tty->print_cr("Reference counts         %5d", Symbol::_total_count);
+  tty->print_cr("Histogram of symbol length:");
   tty->print_cr("%8s %5d", "Total  ", total);
   tty->print_cr("%8s %5d", "Maximum", max_symbols);
   tty->print_cr("%8s %3.2f", "Average",
@@ -304,22 +398,41 @@
       tty->print_cr("%6d %10d", i, results[i]);
     }
   }
-  int line_length = 70;
-  tty->print_cr("%s %30s", " Length", "Number chains that length");
-  for (i = 0; i < results_length; i++) {
-    if (results[i] > 0) {
-      tty->print("%4d", i);
-      for (j = 0; (j < results[i]) && (j < line_length);  j++) {
-        tty->print("%1s", "*");
+  if (Verbose) {
+    int line_length = 70;
+    tty->print_cr("%s %30s", " Length", "Number chains that length");
+    for (i = 0; i < results_length; i++) {
+      if (results[i] > 0) {
+        tty->print("%4d", i);
+        for (j = 0; (j < results[i]) && (j < line_length);  j++) {
+          tty->print("%1s", "*");
+        }
+        if (j == line_length) {
+          tty->print("%1s", "+");
+        }
+        tty->cr();
       }
-      if (j == line_length) {
-        tty->print("%1s", "+");
+    }
+  }
+  tty->print_cr(" %s %d: %d\n", "Number chains longer than",
+                    results_length, out_of_range);
+}
+
+void SymbolTable::print() {
+  for (int i = 0; i < the_table()->table_size(); ++i) {
+    HashtableEntry<Symbol*>** p = the_table()->bucket_addr(i);
+    HashtableEntry<Symbol*>* entry = the_table()->bucket(i);
+    if (entry != NULL) {
+      while (entry != NULL) {
+        tty->print(PTR_FORMAT " ", entry->literal());
+        entry->literal()->print();
+        tty->print(" %d", entry->literal()->refcount());
+        p = entry->next_addr();
+        entry = (HashtableEntry<Symbol*>*)HashtableEntry<Symbol*>::make_ptr(*p);
       }
       tty->cr();
     }
   }
-  tty->print_cr(" %s %d: %d\n", "Number chains longer than",
-                    results_length, out_of_range);
 }
 
 #endif // PRODUCT
@@ -396,7 +509,7 @@
 
 oop StringTable::lookup(int index, jchar* name,
                         int len, unsigned int hash) {
-  for (HashtableEntry* l = bucket(index); l != NULL; l = l->next()) {
+  for (HashtableEntry<oop>* l = bucket(index); l != NULL; l = l->next()) {
     if (l->hash() == hash) {
       if (java_lang_String::equals(l->literal(), name, len)) {
         return l->literal();
@@ -436,13 +549,13 @@
     return test;
   }
 
-  HashtableEntry* entry = new_entry(hashValue, string());
+  HashtableEntry<oop>* entry = new_entry(hashValue, string());
   add_entry(index, entry);
   return string();
 }
 
 
-oop StringTable::lookup(symbolOop symbol) {
+oop StringTable::lookup(Symbol* symbol) {
   ResourceMark rm;
   int length;
   jchar* chars = symbol->as_unicode(length);
@@ -466,7 +579,7 @@
                                 hashValue, CHECK_NULL);
 }
 
-oop StringTable::intern(symbolOop symbol, TRAPS) {
+oop StringTable::intern(Symbol* symbol, TRAPS) {
   if (symbol == NULL) return NULL;
   ResourceMark rm(THREAD);
   int length;
@@ -500,9 +613,50 @@
   return result;
 }
 
+void StringTable::unlink(BoolObjectClosure* is_alive) {
+  // Readers of the table are unlocked, so we should only be removing
+  // entries at a safepoint.
+  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
+  for (int i = 0; i < the_table()->table_size(); ++i) {
+    for (HashtableEntry<oop>** p = the_table()->bucket_addr(i); *p != NULL; ) {
+      HashtableEntry<oop>* entry = *p;
+      if (entry->is_shared()) {
+        break;
+      }
+      assert(entry->literal() != NULL, "just checking");
+      if (is_alive->do_object_b(entry->literal())) {
+        p = entry->next_addr();
+      } else {
+        *p = entry->next();
+        the_table()->free_entry(entry);
+      }
+    }
+  }
+}
+
+void StringTable::oops_do(OopClosure* f) {
+  for (int i = 0; i < the_table()->table_size(); ++i) {
+    HashtableEntry<oop>** p = the_table()->bucket_addr(i);
+    HashtableEntry<oop>* entry = the_table()->bucket(i);
+    while (entry != NULL) {
+      f->do_oop((oop*)entry->literal_addr());
+
+      // Did the closure remove the literal from the table?
+      if (entry->literal() == NULL) {
+        assert(!entry->is_shared(), "immutable hashtable entry?");
+        *p = entry->next();
+        the_table()->free_entry(entry);
+      } else {
+        p = entry->next_addr();
+      }
+      entry = (HashtableEntry<oop>*)HashtableEntry<oop>::make_ptr(*p);
+    }
+  }
+}
+
 void StringTable::verify() {
   for (int i = 0; i < the_table()->table_size(); ++i) {
-    HashtableEntry* p = the_table()->bucket(i);
+    HashtableEntry<oop>* p = the_table()->bucket(i);
     for ( ; p != NULL; p = p->next()) {
       oop s = p->literal();
       guarantee(s != NULL, "interned string is NULL");