< prev index next >

src/share/vm/classfile/compactHashtable.hpp

Print this page

        

*** 26,35 **** --- 26,36 ---- #define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" #include "memory/allocation.inline.hpp" + #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "services/diagnosticCommand.hpp" #include "utilities/hashtable.hpp" class NumberSeq;
*** 47,72 **** // // The compact hash table writer. Used at dump time for writing out // the compact table to the shared archive. // // At dump time, the CompactHashtableWriter obtains all entries from the ! // symbol table and adds them to a new temporary hash table. The hash // table size (number of buckets) is calculated using // '(num_entries + bucket_size - 1) / bucket_size'. The default bucket // size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option. // 4 is chosen because it produces smaller sized bucket on average for // faster lookup. It also has relatively small number of empty buckets and // good distribution of the entries. // ! // We use a simple hash function (symbol_hash % num_bucket) for the table. // The new table is compacted when written out. Please see comments // above the CompactHashtable class for the table layout detail. The bucket // offsets are written to the archive as part of the compact table. The // bucket offset is encoded in the low 30-bit (0-29) and the bucket type // (regular or compact) are encoded in bit[31, 30]. For buckets with more ! // than one entry, both symbol hash and symbol offset are written to the ! // table. For buckets with only one entry, only the symbol offset is written // to the table and the buckets are tagged as compact in their type bits. // Buckets without entry are skipped from the table. Their offsets are // still written out for faster lookup. // class CompactHashtableWriter: public StackObj { --- 48,73 ---- // // The compact hash table writer. Used at dump time for writing out // the compact table to the shared archive. // // At dump time, the CompactHashtableWriter obtains all entries from the ! // symbol/string table and adds them to a new temporary hash table. The hash // table size (number of buckets) is calculated using // '(num_entries + bucket_size - 1) / bucket_size'. The default bucket // size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option. // 4 is chosen because it produces smaller sized bucket on average for // faster lookup. It also has relatively small number of empty buckets and // good distribution of the entries. // ! // We use a simple hash function (hash % num_bucket) for the table. // The new table is compacted when written out. Please see comments // above the CompactHashtable class for the table layout detail. The bucket // offsets are written to the archive as part of the compact table. The // bucket offset is encoded in the low 30-bit (0-29) and the bucket type // (regular or compact) are encoded in bit[31, 30]. For buckets with more ! // than one entry, both hash and entry offset are written to the ! // table. For buckets with only one entry, only the entry offset is written // to the table and the buckets are tagged as compact in their type bits. // Buckets without entry are skipped from the table. Their offsets are // still written out for faster lookup. // class CompactHashtableWriter: public StackObj {
*** 76,130 **** unsigned int _hash; void* _literal; public: Entry(unsigned int hash, Symbol *symbol) : _next(NULL), _hash(hash), _literal(symbol) {} void *value() { return _literal; } Symbol *symbol() { return (Symbol*)_literal; } unsigned int hash() { return _hash; } Entry *next() {return _next;} void set_next(Entry *p) {_next = p;} }; // class CompactHashtableWriter::Entry private: static int number_of_buckets(int num_entries); ! const char* _table_name; int _num_entries; int _num_buckets; juint* _bucket_sizes; Entry** _buckets; int _required_bytes; CompactHashtableStats* _stats; public: // This is called at dump-time only ! CompactHashtableWriter(const char* table_name, int num_entries, CompactHashtableStats* stats); ~CompactHashtableWriter(); int get_required_bytes() { return _required_bytes; } void add(unsigned int hash, Symbol* symbol) { add(hash, new Entry(hash, symbol)); } private: void add(unsigned int hash, Entry* entry); juint* dump_table(juint* p, juint** first_bucket, NumberSeq* summary); juint* dump_buckets(juint* table, juint* p, NumberSeq* summary); public: void dump(char** top, char* end); }; #define REGULAR_BUCKET_TYPE 0 #define COMPACT_BUCKET_TYPE 1 #define TABLEEND_BUCKET_TYPE 3 --- 77,140 ---- unsigned int _hash; void* _literal; public: Entry(unsigned int hash, Symbol *symbol) : _next(NULL), _hash(hash), _literal(symbol) {} + Entry(unsigned int hash, oop string) : _next(NULL), _hash(hash), _literal(string) {} void *value() { return _literal; } Symbol *symbol() { return (Symbol*)_literal; } + oop string() { + return (oop)_literal; + } unsigned int hash() { return _hash; } Entry *next() {return _next;} void set_next(Entry *p) {_next = p;} }; // class CompactHashtableWriter::Entry private: static int number_of_buckets(int num_entries); ! int _type; int _num_entries; int _num_buckets; juint* _bucket_sizes; Entry** _buckets; int _required_bytes; CompactHashtableStats* _stats; public: // This is called at dump-time only ! CompactHashtableWriter(int table_type, int num_entries, CompactHashtableStats* stats); ~CompactHashtableWriter(); int get_required_bytes() { return _required_bytes; } void add(unsigned int hash, Symbol* symbol) { add(hash, new Entry(hash, symbol)); } + void add(unsigned int hash, oop string) { + add(hash, new Entry(hash, string)); + } + private: void add(unsigned int hash, Entry* entry); juint* dump_table(juint* p, juint** first_bucket, NumberSeq* summary); juint* dump_buckets(juint* table, juint* p, NumberSeq* summary); public: void dump(char** top, char* end); + const char* table_name(); }; #define REGULAR_BUCKET_TYPE 0 #define COMPACT_BUCKET_TYPE 1 #define TABLEEND_BUCKET_TYPE 3
*** 134,160 **** #define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT) #define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK)) ///////////////////////////////////////////////////////////////////////////// // ! // CompactHashtable is used to stored the CDS archive's symbol table. Used // at runtime only to access the compact table from the archive. // // Because these tables are read-only (no entries can be added/deleted) at run-time // and tend to have large number of entries, we try to minimize the footprint // cost per entry. // ! // Layout of compact symbol table in the shared archive: // // uintx base_address; ! // juint num_symbols; // juint num_buckets; // juint bucket_infos[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset // juint table[] // // ----------------------------------- ! // | base_address | num_symbols | // |---------------------------------| // | num_buckets | bucket_info0 | // |---------------------------------| // | bucket_info1 | bucket_info2 | // | bucket_info3 ... | --- 144,170 ---- #define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT) #define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK)) ///////////////////////////////////////////////////////////////////////////// // ! // CompactHashtable is used to stored the CDS archive's symbol/string table. Used // at runtime only to access the compact table from the archive. // // Because these tables are read-only (no entries can be added/deleted) at run-time // and tend to have large number of entries, we try to minimize the footprint // cost per entry. // ! // Layout of compact table in the shared archive: // // uintx base_address; ! // juint num_entries; // juint num_buckets; // juint bucket_infos[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset // juint table[] // // ----------------------------------- ! // | base_address | num_entries | // |---------------------------------| // | num_buckets | bucket_info0 | // |---------------------------------| // | bucket_info1 | bucket_info2 | // | bucket_info3 ... |
*** 175,219 **** // // There are two types of buckets, regular buckets and compact buckets. The // compact buckets have '01' in their highest 2-bit, and regular buckets have // '00' in their highest 2-bit. // ! // For normal buckets, each symbol's entry is 8 bytes in the table[]: ! // juint hash; /* symbol hash */ // juint offset; /* Symbol* sym = (Symbol*)(base_address + offset) */ // // For compact buckets, each entry has only the 4-byte 'offset' in the table[]. // // See CompactHashtable::lookup() for how the table is searched at runtime. // See CompactHashtableWriter::dump() for how the table is written at CDS // dump time. // template <class T, class N> class CompactHashtable VALUE_OBJ_CLASS_SPEC { friend class VMStructs; uintx _base_address; juint _entry_count; juint _bucket_count; juint _table_end_offset; juint* _buckets; ! inline bool equals(T entry, const char* name, int len) { ! if (entry->equals(name, len)) { ! assert(entry->refcount() == -1, "must be shared"); ! return true; ! } else { ! return false; } } public: CompactHashtable() { _entry_count = 0; _bucket_count = 0; _table_end_offset = 0; _buckets = 0; } ! const char* init(const char *buffer); // Lookup an entry from the compact table inline T lookup(const N* name, unsigned int hash, int len) { if (_entry_count > 0) { assert(!DumpSharedSpaces, "run-time only"); --- 185,262 ---- // // There are two types of buckets, regular buckets and compact buckets. The // compact buckets have '01' in their highest 2-bit, and regular buckets have // '00' in their highest 2-bit. // ! // For normal buckets, each entry is 8 bytes in the table[]: ! // juint hash; /* symbol/string hash */ ! // union { // juint offset; /* Symbol* sym = (Symbol*)(base_address + offset) */ + // narrowOop str; /* String narrowOop encoding */ + // } + // // // For compact buckets, each entry has only the 4-byte 'offset' in the table[]. // // See CompactHashtable::lookup() for how the table is searched at runtime. // See CompactHashtableWriter::dump() for how the table is written at CDS // dump time. // template <class T, class N> class CompactHashtable VALUE_OBJ_CLASS_SPEC { friend class VMStructs; + + public: + enum CompactHashtableType { + _symbol_table = 0, + _string_table = 1 + }; + + private: + CompactHashtableType _type; uintx _base_address; juint _entry_count; juint _bucket_count; juint _table_end_offset; juint* _buckets; ! inline Symbol* lookup_entry(CompactHashtable<Symbol*, char>* const t, ! juint* addr, const char* name, int len) { ! Symbol* sym = (Symbol*)((void*)(_base_address + *addr)); ! if (sym->equals(name, len)) { ! assert(sym->refcount() == -1, "must be shared"); ! return sym; } + + return NULL; + } + + inline oop lookup_entry(CompactHashtable<oop, char>* const t, + juint* addr, const char* name, int len) { + narrowOop obj = (narrowOop)(*addr); + oop string = oopDesc::decode_heap_oop(obj); + if (java_lang_String::equals(string, (jchar*)name, len)) { + return string; + } + + return NULL; } public: CompactHashtable() { _entry_count = 0; _bucket_count = 0; _table_end_offset = 0; _buckets = 0; } ! const char* init(CompactHashtableType type, const char *buffer); ! ! void reset() { ! _entry_count = 0; ! _bucket_count = 0; ! _table_end_offset = 0; ! _buckets = 0; ! } // Lookup an entry from the compact table inline T lookup(const N* name, unsigned int hash, int len) { if (_entry_count > 0) { assert(!DumpSharedSpaces, "run-time only");
*** 223,249 **** int bucket_type = BUCKET_TYPE(bucket_info); juint* bucket = _buckets + bucket_offset; juint* bucket_end = _buckets; if (bucket_type == COMPACT_BUCKET_TYPE) { ! // the compact bucket has one entry with symbol offset only ! T entry = (T)((void*)(_base_address + bucket[0])); ! if (equals(entry, name, len)) { ! return entry; } } else { // This is a regular bucket, which has more than one ! // entries. Each entry is a pair of symbol (hash, offset). // Seek until the end of the bucket. bucket_end += BUCKET_OFFSET(_buckets[index + 1]); while (bucket < bucket_end) { unsigned int h = (unsigned int)(bucket[0]); if (h == hash) { ! juint offset = bucket[1]; ! T entry = (T)((void*)(_base_address + offset)); ! if (equals(entry, name, len)) { ! return entry; } } bucket += 2; } } --- 266,291 ---- int bucket_type = BUCKET_TYPE(bucket_info); juint* bucket = _buckets + bucket_offset; juint* bucket_end = _buckets; if (bucket_type == COMPACT_BUCKET_TYPE) { ! // the compact bucket has one entry with entry offset only ! T res = lookup_entry(this, &bucket[0], name, len); ! if (res != NULL) { ! return res; } } else { // This is a regular bucket, which has more than one ! // entries. Each entry is a pair of entry (hash, offset). // Seek until the end of the bucket. bucket_end += BUCKET_OFFSET(_buckets[index + 1]); while (bucket < bucket_end) { unsigned int h = (unsigned int)(bucket[0]); if (h == hash) { ! T res = lookup_entry(this, &bucket[1], name, len); ! if (res != NULL) { ! return res; } } bucket += 2; } }
*** 251,322 **** return NULL; } // iterate over symbols void symbols_do(SymbolClosure *cl); }; //////////////////////////////////////////////////////////////////////// // // Read/Write the contents of a hashtable textual dump (created by ! // SymbolTable::dump). // Because the dump file may be big (hundred of MB in extreme cases), // we use mmap for fast access when reading it. // class HashtableTextDump VALUE_OBJ_CLASS_SPEC { int _fd; const char* _base; const char* _p; const char* _end; const char* _filename; size_t _size; public: HashtableTextDump(const char* filename); ~HashtableTextDump(); void quit(const char* err, const char* msg); inline int remain() { return (int)(_end - _p); } ! void corrupted(const char *p); inline void corrupted_if(bool cond) { if (cond) { ! corrupted(_p); } } bool skip_newline(); int skip(char must_be_char); void skip_past(char c); void check_version(const char* ver); ! inline int get_num(char delim) { const char* p = _p; const char* end = _end; int num = 0; while (p < end) { char c = *p ++; if ('0' <= c && c <= '9') { num = num * 10 + (c - '0'); } else if (c == delim) { _p = p; ! return num; } else { ! corrupted(p-1); } } ! corrupted(_end); ShouldNotReachHere(); ! return 0; } ! int scan_prefix(); ! int scan_prefix2(); jchar unescape(const char* from, const char* end, int count); void get_utf8(char* utf8_buffer, int utf8_length); static void put_utf8(outputStream* st, const char* utf8_string, int utf8_length); }; --- 293,379 ---- return NULL; } // iterate over symbols void symbols_do(SymbolClosure *cl); + + // iterate over strings + void oops_do(OopClosure* f); }; //////////////////////////////////////////////////////////////////////// // // Read/Write the contents of a hashtable textual dump (created by ! // SymbolTable::dump and StringTable::dump). // Because the dump file may be big (hundred of MB in extreme cases), // we use mmap for fast access when reading it. // class HashtableTextDump VALUE_OBJ_CLASS_SPEC { int _fd; const char* _base; const char* _p; const char* _end; const char* _filename; size_t _size; + int _prefix_type; + int _line_no; public: HashtableTextDump(const char* filename); ~HashtableTextDump(); + enum { + SymbolPrefix = 1 << 0, + StringPrefix = 1 << 1, + Unknown = 1 << 2 + }; + void quit(const char* err, const char* msg); inline int remain() { return (int)(_end - _p); } ! void corrupted(const char *p, const char *msg); inline void corrupted_if(bool cond) { if (cond) { ! corrupted(_p, NULL); } } bool skip_newline(); int skip(char must_be_char); void skip_past(char c); void check_version(const char* ver); ! inline bool get_num(char delim, int *utf8_length) { const char* p = _p; const char* end = _end; int num = 0; while (p < end) { char c = *p ++; if ('0' <= c && c <= '9') { num = num * 10 + (c - '0'); } else if (c == delim) { _p = p; ! *utf8_length = num; ! return true; } else { ! // Not [0-9], not 'delim' ! return false; } } ! corrupted(_end, "Incorrect format"); ShouldNotReachHere(); ! return false; } ! void scan_prefix_type(); ! int scan_prefix(int* utf8_length); ! int scan_string_prefix(); ! int scan_symbol_prefix(); jchar unescape(const char* from, const char* end, int count); void get_utf8(char* utf8_buffer, int utf8_length); static void put_utf8(outputStream* st, const char* utf8_string, int utf8_length); };
< prev index next >