--- 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");