mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-25 22:04:51 +02:00
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:
parent
950858350d
commit
7b4f8073f0
223 changed files with 3783 additions and 3641 deletions
|
@ -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");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue