8056084: Refactor Hashtable to allow implementations without rehashing support
Reviewed-by: gziemski, jmasa, brutisso, coleenp, tschatzl
This commit is contained in:
parent
f12e5848bd
commit
6fc8764c65
@ -109,7 +109,7 @@ oop StringTable::lookup(int index, jchar* name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the bucket size is too deep check if this hash code is insufficient.
|
// If the bucket size is too deep check if this hash code is insufficient.
|
||||||
if (count >= BasicHashtable<mtSymbol>::rehash_count && !needs_rehashing()) {
|
if (count >= rehash_count && !needs_rehashing()) {
|
||||||
_needs_rehashing = check_rehash_table(count);
|
_needs_rehashing = check_rehash_table(count);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "utilities/hashtable.hpp"
|
#include "utilities/hashtable.hpp"
|
||||||
|
|
||||||
class StringTable : public Hashtable<oop, mtSymbol> {
|
class StringTable : public RehashableHashtable<oop, mtSymbol> {
|
||||||
friend class VMStructs;
|
friend class VMStructs;
|
||||||
friend class Symbol;
|
friend class Symbol;
|
||||||
|
|
||||||
@ -55,11 +55,11 @@ private:
|
|||||||
// in the range [start_idx, end_idx).
|
// in the range [start_idx, end_idx).
|
||||||
static void buckets_unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int start_idx, int end_idx, int* processed, int* removed);
|
static void buckets_unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int start_idx, int end_idx, int* processed, int* removed);
|
||||||
|
|
||||||
StringTable() : Hashtable<oop, mtSymbol>((int)StringTableSize,
|
StringTable() : RehashableHashtable<oop, mtSymbol>((int)StringTableSize,
|
||||||
sizeof (HashtableEntry<oop, mtSymbol>)) {}
|
sizeof (HashtableEntry<oop, mtSymbol>)) {}
|
||||||
|
|
||||||
StringTable(HashtableBucket<mtSymbol>* t, int number_of_entries)
|
StringTable(HashtableBucket<mtSymbol>* t, int number_of_entries)
|
||||||
: Hashtable<oop, mtSymbol>((int)StringTableSize, sizeof (HashtableEntry<oop, mtSymbol>), t,
|
: RehashableHashtable<oop, mtSymbol>((int)StringTableSize, sizeof (HashtableEntry<oop, mtSymbol>), t,
|
||||||
number_of_entries) {}
|
number_of_entries) {}
|
||||||
public:
|
public:
|
||||||
// The string table
|
// The string table
|
||||||
|
@ -201,7 +201,7 @@ Symbol* SymbolTable::lookup(int index, const char* name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the bucket size is too deep check if this hash code is insufficient.
|
// If the bucket size is too deep check if this hash code is insufficient.
|
||||||
if (count >= BasicHashtable<mtSymbol>::rehash_count && !needs_rehashing()) {
|
if (count >= rehash_count && !needs_rehashing()) {
|
||||||
_needs_rehashing = check_rehash_table(count);
|
_needs_rehashing = check_rehash_table(count);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -73,7 +73,7 @@ class TempNewSymbol : public StackObj {
|
|||||||
operator Symbol*() { return _temp; }
|
operator Symbol*() { return _temp; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class SymbolTable : public Hashtable<Symbol*, mtSymbol> {
|
class SymbolTable : public RehashableHashtable<Symbol*, mtSymbol> {
|
||||||
friend class VMStructs;
|
friend class VMStructs;
|
||||||
friend class ClassFileParser;
|
friend class ClassFileParser;
|
||||||
|
|
||||||
@ -109,10 +109,10 @@ private:
|
|||||||
Symbol* lookup(int index, const char* name, int len, unsigned int hash);
|
Symbol* lookup(int index, const char* name, int len, unsigned int hash);
|
||||||
|
|
||||||
SymbolTable()
|
SymbolTable()
|
||||||
: Hashtable<Symbol*, mtSymbol>(SymbolTableSize, sizeof (HashtableEntry<Symbol*, mtSymbol>)) {}
|
: RehashableHashtable<Symbol*, mtSymbol>(SymbolTableSize, sizeof (HashtableEntry<Symbol*, mtSymbol>)) {}
|
||||||
|
|
||||||
SymbolTable(HashtableBucket<mtSymbol>* t, int number_of_entries)
|
SymbolTable(HashtableBucket<mtSymbol>* t, int number_of_entries)
|
||||||
: Hashtable<Symbol*, mtSymbol>(SymbolTableSize, sizeof (HashtableEntry<Symbol*, mtSymbol>), t,
|
: RehashableHashtable<Symbol*, mtSymbol>(SymbolTableSize, sizeof (HashtableEntry<Symbol*, mtSymbol>), t,
|
||||||
number_of_entries) {}
|
number_of_entries) {}
|
||||||
|
|
||||||
// Arena for permanent symbols (null class loader) that are never unloaded
|
// Arena for permanent symbols (null class loader) that are never unloaded
|
||||||
|
@ -37,21 +37,22 @@
|
|||||||
#include "utilities/numberSeq.hpp"
|
#include "utilities/numberSeq.hpp"
|
||||||
|
|
||||||
|
|
||||||
// This is a generic hashtable, designed to be used for the symbol
|
// This hashtable is implemented as an open hash table with a fixed number of buckets.
|
||||||
// and string tables.
|
|
||||||
//
|
|
||||||
// It is implemented as an open hash table with a fixed number of buckets.
|
|
||||||
//
|
|
||||||
// %note:
|
|
||||||
// - HashtableEntrys are allocated in blocks to reduce the space overhead.
|
|
||||||
|
|
||||||
template <MEMFLAGS F> BasicHashtableEntry<F>* BasicHashtable<F>::new_entry(unsigned int hashValue) {
|
template <MEMFLAGS F> BasicHashtableEntry<F>* BasicHashtable<F>::new_entry_free_list() {
|
||||||
BasicHashtableEntry<F>* entry;
|
BasicHashtableEntry<F>* entry = NULL;
|
||||||
|
if (_free_list != NULL) {
|
||||||
if (_free_list) {
|
|
||||||
entry = _free_list;
|
entry = _free_list;
|
||||||
_free_list = _free_list->next();
|
_free_list = _free_list->next();
|
||||||
} else {
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashtableEntrys are allocated in blocks to reduce the space overhead.
|
||||||
|
template <MEMFLAGS F> BasicHashtableEntry<F>* BasicHashtable<F>::new_entry(unsigned int hashValue) {
|
||||||
|
BasicHashtableEntry<F>* entry = new_entry_free_list();
|
||||||
|
|
||||||
|
if (entry == NULL) {
|
||||||
if (_first_free_entry + _entry_size >= _end_block) {
|
if (_first_free_entry + _entry_size >= _end_block) {
|
||||||
int block_size = MIN2(512, MAX2((int)_table_size / 2, (int)_number_of_entries));
|
int block_size = MIN2(512, MAX2((int)_table_size / 2, (int)_number_of_entries));
|
||||||
int len = _entry_size * block_size;
|
int len = _entry_size * block_size;
|
||||||
@ -84,9 +85,9 @@ template <class T, MEMFLAGS F> HashtableEntry<T, F>* Hashtable<T, F>::new_entry(
|
|||||||
// This is somewhat an arbitrary heuristic but if one bucket gets to
|
// This is somewhat an arbitrary heuristic but if one bucket gets to
|
||||||
// rehash_count which is currently 100, there's probably something wrong.
|
// rehash_count which is currently 100, there's probably something wrong.
|
||||||
|
|
||||||
template <MEMFLAGS F> bool BasicHashtable<F>::check_rehash_table(int count) {
|
template <class T, MEMFLAGS F> bool RehashableHashtable<T, F>::check_rehash_table(int count) {
|
||||||
assert(table_size() != 0, "underflow");
|
assert(this->table_size() != 0, "underflow");
|
||||||
if (count > (((double)number_of_entries()/(double)table_size())*rehash_multiple)) {
|
if (count > (((double)this->number_of_entries()/(double)this->table_size())*rehash_multiple)) {
|
||||||
// Set a flag for the next safepoint, which should be at some guaranteed
|
// Set a flag for the next safepoint, which should be at some guaranteed
|
||||||
// safepoint interval.
|
// safepoint interval.
|
||||||
return true;
|
return true;
|
||||||
@ -94,13 +95,13 @@ template <MEMFLAGS F> bool BasicHashtable<F>::check_rehash_table(int count) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, MEMFLAGS F> juint Hashtable<T, F>::_seed = 0;
|
template <class T, MEMFLAGS F> juint RehashableHashtable<T, F>::_seed = 0;
|
||||||
|
|
||||||
// Create a new table and using alternate hash code, populate the new table
|
// Create a new table and using alternate hash code, populate the new table
|
||||||
// with the existing elements. This can be used to change the hash code
|
// with the existing elements. This can be used to change the hash code
|
||||||
// and could in the future change the size of the table.
|
// and could in the future change the size of the table.
|
||||||
|
|
||||||
template <class T, MEMFLAGS F> void Hashtable<T, F>::move_to(Hashtable<T, F>* new_table) {
|
template <class T, MEMFLAGS F> void RehashableHashtable<T, F>::move_to(RehashableHashtable<T, F>* new_table) {
|
||||||
|
|
||||||
// Initialize the global seed for hashing.
|
// Initialize the global seed for hashing.
|
||||||
_seed = AltHashing::compute_seed();
|
_seed = AltHashing::compute_seed();
|
||||||
@ -110,7 +111,7 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::move_to(Hashtable<T, F>* ne
|
|||||||
|
|
||||||
// Iterate through the table and create a new entry for the new table
|
// Iterate through the table and create a new entry for the new table
|
||||||
for (int i = 0; i < new_table->table_size(); ++i) {
|
for (int i = 0; i < new_table->table_size(); ++i) {
|
||||||
for (HashtableEntry<T, F>* p = bucket(i); p != NULL; ) {
|
for (HashtableEntry<T, F>* p = this->bucket(i); p != NULL; ) {
|
||||||
HashtableEntry<T, F>* next = p->next();
|
HashtableEntry<T, F>* next = p->next();
|
||||||
T string = p->literal();
|
T string = p->literal();
|
||||||
// Use alternate hashing algorithm on the symbol in the first table
|
// Use alternate hashing algorithm on the symbol in the first table
|
||||||
@ -239,11 +240,11 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::reverse(void* boundary) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, MEMFLAGS F> int Hashtable<T, F>::literal_size(Symbol *symbol) {
|
template <class T, MEMFLAGS F> int RehashableHashtable<T, F>::literal_size(Symbol *symbol) {
|
||||||
return symbol->size() * HeapWordSize;
|
return symbol->size() * HeapWordSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T, MEMFLAGS F> int Hashtable<T, F>::literal_size(oop oop) {
|
template <class T, MEMFLAGS F> int RehashableHashtable<T, F>::literal_size(oop oop) {
|
||||||
// NOTE: this would over-count if (pre-JDK8) java_lang_Class::has_offset_field() is true,
|
// NOTE: this would over-count if (pre-JDK8) java_lang_Class::has_offset_field() is true,
|
||||||
// and the String.value array is shared by several Strings. However, starting from JDK8,
|
// and the String.value array is shared by several Strings. However, starting from JDK8,
|
||||||
// the String.value array is not shared anymore.
|
// the String.value array is not shared anymore.
|
||||||
@ -256,12 +257,12 @@ template <class T, MEMFLAGS F> int Hashtable<T, F>::literal_size(oop oop) {
|
|||||||
// Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
|
// Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
|
||||||
// add a new function Hashtable<T, F>::literal_size(MyNewType lit)
|
// add a new function Hashtable<T, F>::literal_size(MyNewType lit)
|
||||||
|
|
||||||
template <class T, MEMFLAGS F> void Hashtable<T, F>::dump_table(outputStream* st, const char *table_name) {
|
template <class T, MEMFLAGS F> void RehashableHashtable<T, F>::dump_table(outputStream* st, const char *table_name) {
|
||||||
NumberSeq summary;
|
NumberSeq summary;
|
||||||
int literal_bytes = 0;
|
int literal_bytes = 0;
|
||||||
for (int i = 0; i < this->table_size(); ++i) {
|
for (int i = 0; i < this->table_size(); ++i) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (HashtableEntry<T, F>* e = bucket(i);
|
for (HashtableEntry<T, F>* e = this->bucket(i);
|
||||||
e != NULL; e = e->next()) {
|
e != NULL; e = e->next()) {
|
||||||
count++;
|
count++;
|
||||||
literal_bytes += literal_size(e->literal());
|
literal_bytes += literal_size(e->literal());
|
||||||
@ -271,7 +272,7 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::dump_table(outputStream* st
|
|||||||
double num_buckets = summary.num();
|
double num_buckets = summary.num();
|
||||||
double num_entries = summary.sum();
|
double num_entries = summary.sum();
|
||||||
|
|
||||||
int bucket_bytes = (int)num_buckets * sizeof(bucket(0));
|
int bucket_bytes = (int)num_buckets * sizeof(HashtableBucket<F>);
|
||||||
int entry_bytes = (int)num_entries * sizeof(HashtableEntry<T, F>);
|
int entry_bytes = (int)num_entries * sizeof(HashtableEntry<T, F>);
|
||||||
int total_bytes = literal_bytes + bucket_bytes + entry_bytes;
|
int total_bytes = literal_bytes + bucket_bytes + entry_bytes;
|
||||||
|
|
||||||
@ -355,11 +356,14 @@ template <MEMFLAGS F> void BasicHashtable<F>::verify_lookup_length(double load)
|
|||||||
|
|
||||||
// Explicitly instantiate these types
|
// Explicitly instantiate these types
|
||||||
template class Hashtable<ConstantPool*, mtClass>;
|
template class Hashtable<ConstantPool*, mtClass>;
|
||||||
|
template class RehashableHashtable<Symbol*, mtSymbol>;
|
||||||
|
template class RehashableHashtable<oopDesc*, mtSymbol>;
|
||||||
template class Hashtable<Symbol*, mtSymbol>;
|
template class Hashtable<Symbol*, mtSymbol>;
|
||||||
template class Hashtable<Klass*, mtClass>;
|
template class Hashtable<Klass*, mtClass>;
|
||||||
template class Hashtable<oop, mtClass>;
|
template class Hashtable<oop, mtClass>;
|
||||||
#if defined(SOLARIS) || defined(CHECK_UNHANDLED_OOPS)
|
#if defined(SOLARIS) || defined(CHECK_UNHANDLED_OOPS)
|
||||||
template class Hashtable<oop, mtSymbol>;
|
template class Hashtable<oop, mtSymbol>;
|
||||||
|
template class RehashableHashtable<oop, mtSymbol>;
|
||||||
#endif // SOLARIS || CHECK_UNHANDLED_OOPS
|
#endif // SOLARIS || CHECK_UNHANDLED_OOPS
|
||||||
template class Hashtable<oopDesc*, mtSymbol>;
|
template class Hashtable<oopDesc*, mtSymbol>;
|
||||||
template class Hashtable<Symbol*, mtClass>;
|
template class Hashtable<Symbol*, mtClass>;
|
||||||
|
@ -178,11 +178,6 @@ protected:
|
|||||||
void verify_lookup_length(double load);
|
void verify_lookup_length(double load);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum {
|
|
||||||
rehash_count = 100,
|
|
||||||
rehash_multiple = 60
|
|
||||||
};
|
|
||||||
|
|
||||||
void initialize(int table_size, int entry_size, int number_of_entries);
|
void initialize(int table_size, int entry_size, int number_of_entries);
|
||||||
|
|
||||||
// Accessor
|
// Accessor
|
||||||
@ -194,12 +189,12 @@ protected:
|
|||||||
// The following method is not MT-safe and must be done under lock.
|
// The following method is not MT-safe and must be done under lock.
|
||||||
BasicHashtableEntry<F>** bucket_addr(int i) { return _buckets[i].entry_addr(); }
|
BasicHashtableEntry<F>** bucket_addr(int i) { return _buckets[i].entry_addr(); }
|
||||||
|
|
||||||
|
// Attempt to get an entry from the free list
|
||||||
|
BasicHashtableEntry<F>* new_entry_free_list();
|
||||||
|
|
||||||
// Table entry management
|
// Table entry management
|
||||||
BasicHashtableEntry<F>* new_entry(unsigned int hashValue);
|
BasicHashtableEntry<F>* new_entry(unsigned int hashValue);
|
||||||
|
|
||||||
// Check that the table is unbalanced
|
|
||||||
bool check_rehash_table(int count);
|
|
||||||
|
|
||||||
// Used when moving the entry to another table
|
// Used when moving the entry to another table
|
||||||
// Clean up links, but do not add to free_list
|
// Clean up links, but do not add to free_list
|
||||||
void unlink_entry(BasicHashtableEntry<F>* entry) {
|
void unlink_entry(BasicHashtableEntry<F>* entry) {
|
||||||
@ -277,8 +272,30 @@ protected:
|
|||||||
return (HashtableEntry<T, F>**)BasicHashtable<F>::bucket_addr(i);
|
return (HashtableEntry<T, F>**)BasicHashtable<F>::bucket_addr(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, MEMFLAGS F> class RehashableHashtable : public Hashtable<T, F> {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
enum {
|
||||||
|
rehash_count = 100,
|
||||||
|
rehash_multiple = 60
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that the table is unbalanced
|
||||||
|
bool check_rehash_table(int count);
|
||||||
|
|
||||||
|
public:
|
||||||
|
RehashableHashtable(int table_size, int entry_size)
|
||||||
|
: Hashtable<T, F>(table_size, entry_size) { }
|
||||||
|
|
||||||
|
RehashableHashtable(int table_size, int entry_size,
|
||||||
|
HashtableBucket<F>* buckets, int number_of_entries)
|
||||||
|
: Hashtable<T, F>(table_size, entry_size, buckets, number_of_entries) { }
|
||||||
|
|
||||||
|
|
||||||
// Function to move these elements into the new table.
|
// Function to move these elements into the new table.
|
||||||
void move_to(Hashtable<T, F>* new_table);
|
void move_to(RehashableHashtable<T, F>* new_table);
|
||||||
static bool use_alternate_hashcode() { return _seed != 0; }
|
static bool use_alternate_hashcode() { return _seed != 0; }
|
||||||
static juint seed() { return _seed; }
|
static juint seed() { return _seed; }
|
||||||
|
|
||||||
@ -292,7 +309,6 @@ protected:
|
|||||||
static int literal_size(ConstantPool *cp) {Unimplemented(); return 0;}
|
static int literal_size(ConstantPool *cp) {Unimplemented(); return 0;}
|
||||||
static int literal_size(Klass *k) {Unimplemented(); return 0;}
|
static int literal_size(Klass *k) {Unimplemented(); return 0;}
|
||||||
|
|
||||||
public:
|
|
||||||
void dump_table(outputStream* st, const char *table_name);
|
void dump_table(outputStream* st, const char *table_name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user