diff -r 27e4ea99855d -r 3582bf76420e src/share/vm/classfile/symbolTable.cpp --- a/src/share/vm/classfile/symbolTable.cpp Thu Jan 27 13:42:28 2011 -0800 +++ b/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* 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** p = the_table()->bucket_addr(i); *p != NULL; ) { + HashtableEntry* 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* 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; ihash_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* 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; iequals(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* 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* 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* 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** p = the_table()->bucket_addr(i); + HashtableEntry* 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*)HashtableEntry::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* 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* 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** p = the_table()->bucket_addr(i); *p != NULL; ) { + HashtableEntry* 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** p = the_table()->bucket_addr(i); + HashtableEntry* 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*)HashtableEntry::make_ptr(*p); + } + } +} + void StringTable::verify() { for (int i = 0; i < the_table()->table_size(); ++i) { - HashtableEntry* p = the_table()->bucket(i); + HashtableEntry* p = the_table()->bucket(i); for ( ; p != NULL; p = p->next()) { oop s = p->literal(); guarantee(s != NULL, "interned string is NULL");