6990754: Use native memory and reference counting to implement SymbolTable

Move symbols from permgen into C heap and reference count them

Reviewed-by: never, acorn, jmasa, stefank
This commit is contained in:
Coleen Phillimore 2011-01-27 16:11:27 -08:00
parent 950858350d
commit 7b4f8073f0
223 changed files with 3783 additions and 3641 deletions

View file

@ -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 @@ symbolOop SymbolTable::lookup(int index, const char* name,
// 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 @@ symbolOop SymbolTable::lookup(const char* name, int len, TRAPS) {
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 @@ symbolOop SymbolTable::lookup(symbolHandle sym, int begin, int end, TRAPS) {
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 @@ symbolOop SymbolTable::lookup(symbolHandle sym, int begin, int end, TRAPS) {
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_unicode(const jchar* name, int utf16_length, TRAPS
}
}
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 @@ void SymbolTable::add(constantPoolHandle cp, int names_count,
// 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 @@ symbolOop SymbolTable::basic_add(int index, u1 *name, int len,
// 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 @@ bool SymbolTable::basic_add(constantPoolHandle cp, int names_count,
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 @@ void SymbolTable::print_histogram() {
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 @@ void SymbolTable::print_histogram() {
}
}
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,24 +398,43 @@ void SymbolTable::print_histogram() {
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->cr();
}
}
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();
}
}
}
#endif // PRODUCT
// --------------------------------------------------------------------------
@ -396,7 +509,7 @@ StringTable* StringTable::_the_table = NULL;
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 @@ oop StringTable::basic_add(int index, Handle string_or_null, jchar* name,
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 @@ oop StringTable::intern(Handle string_or_null, jchar* name,
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 @@ oop StringTable::intern(const char* utf8_string, TRAPS) {
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");