--- old/src/share/vm/classfile/compactHashtable.cpp 2015-05-29 14:23:09.757785420 -0400 +++ new/src/share/vm/classfile/compactHashtable.cpp 2015-05-29 14:23:08.145692730 -0400 @@ -32,11 +32,11 @@ // // The compact hash table writer implementations // -CompactHashtableWriter::CompactHashtableWriter(const char* table_name, +CompactHashtableWriter::CompactHashtableWriter(int table_type, int num_entries, CompactHashtableStats* stats) { assert(DumpSharedSpaces, "dump-time only"); - _table_name = table_name; + _type = table_type; _num_entries = num_entries; _num_buckets = number_of_buckets(_num_entries); _buckets = NEW_C_HEAP_ARRAY(Entry*, _num_buckets, mtSymbol); @@ -99,7 +99,7 @@ NumberSeq* summary) { int index; juint* compact_table = p; - // Find the start of the buckets, skip the compact_bucket_infos table + // Compute the start of the buckets, include the compact_bucket_infos table // and the table end offset. juint offset = _num_buckets + 1; *first_bucket = compact_table + offset; @@ -130,10 +130,17 @@ // Write the compact table's entries juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p, NumberSeq* summary) { - uintx base_address = uintx(MetaspaceShared::shared_rs()->base()); - uintx max_delta = uintx(MetaspaceShared::shared_rs()->size()); - assert(max_delta <= 0x7fffffff, "range check"); + uintx base_address = 0; + uintx max_delta = 0; int num_compact_buckets = 0; + if (_type == CompactHashtable::_symbol_table) { + base_address = uintx(MetaspaceShared::shared_rs()->base()); + max_delta = uintx(MetaspaceShared::shared_rs()->size()); + assert(max_delta <= 0x7fffffff, "range check"); + } else { + assert((_type == CompactHashtable::_string_table), "unknown table"); + assert(UseCompressedOops, "UseCompressedOops is required"); + } assert(p != NULL, "sanity"); for (int index = 0; index < _num_buckets; index++) { @@ -148,12 +155,16 @@ for (Entry* tent = _buckets[index]; tent; tent = tent->next()) { if (bucket_type == REGULAR_BUCKET_TYPE) { - *p++ = juint(tent->hash()); // write symbol hash + *p++ = juint(tent->hash()); // write entry hash + } + if (_type == CompactHashtable::_symbol_table) { + uintx deltax = uintx(tent->value()) - base_address; + assert(deltax < max_delta, "range check"); + juint delta = juint(deltax); + *p++ = delta; // write entry offset + } else { + *p++ = oopDesc::encode_heap_oop(tent->string()); } - uintx deltax = uintx(tent->value()) - base_address; - assert(deltax < max_delta, "range check"); - juint delta = juint(deltax); - *p++ = delta; // write symbol offset count ++; } assert(count == _bucket_sizes[index], "sanity"); @@ -174,6 +185,10 @@ uintx base_address = uintx(MetaspaceShared::shared_rs()->base()); + // Now write the following at the beginning of the table: + // base_address (uintx) + // num_entries (juint) + // num_buckets (juint) *p++ = high(base_address); *p++ = low (base_address); // base address *p++ = _num_entries; // number of entries in the table @@ -191,7 +206,8 @@ if (_num_entries > 0) { avg_cost = double(_required_bytes)/double(_num_entries); } - tty->print_cr("Shared %s table stats -------- base: " PTR_FORMAT, _table_name, (intptr_t)base_address); + tty->print_cr("Shared %s table stats -------- base: " PTR_FORMAT, + table_name(), (intptr_t)base_address); tty->print_cr("Number of entries : %9d", _num_entries); tty->print_cr("Total bytes used : %9d", (int)((*top) - old_top)); tty->print_cr("Average bytes per entry : %9.3f", avg_cost); @@ -202,12 +218,24 @@ } } +const char* CompactHashtableWriter::table_name() { + switch (_type) { + case CompactHashtable::_symbol_table: return "symbol"; + case CompactHashtable::_string_table: return "string"; + default: + ; + } + return "unknown"; +} + ///////////////////////////////////////////////////////////// // // The CompactHashtable implementation // -template const char* CompactHashtable::init(const char* buffer) { +template const char* CompactHashtable::init( + CompactHashtableType type, const char* buffer) { assert(!DumpSharedSpaces, "run-time only"); + _type = type; juint*p = (juint*)buffer; juint upper = *p++; juint lower = *p++; @@ -245,8 +273,34 @@ } } +template void CompactHashtable::oops_do(OopClosure* f) { + assert(!DumpSharedSpaces, "run-time only"); + assert(_type == _string_table, "sanity"); + for (juint i = 0; i < _bucket_count; i ++) { + juint bucket_info = _buckets[i]; + juint bucket_offset = BUCKET_OFFSET(bucket_info); + int bucket_type = BUCKET_TYPE(bucket_info); + juint* bucket = _buckets + bucket_offset; + juint* bucket_end = _buckets; + + narrowOop o; + if (bucket_type == COMPACT_BUCKET_TYPE) { + o = (narrowOop)bucket[0]; + f->do_oop(&o); + } else { + bucket_end += BUCKET_OFFSET(_buckets[i + 1]); + while (bucket < bucket_end) { + o = (narrowOop)bucket[1]; + f->do_oop(&o); + bucket += 2; + } + } + } +} + // Explicitly instantiate these types template class CompactHashtable; +template class CompactHashtable; #ifndef O_BINARY // if defined (Win32) use binary files. #define O_BINARY 0 // otherwise do nothing. @@ -273,6 +327,8 @@ _p = _base; _end = _base + st.st_size; _filename = filename; + _prefix_type = Unknown; + _line_no = 1; } HashtableTextDump::~HashtableTextDump() { @@ -286,9 +342,9 @@ vm_exit_during_initialization(err, msg); } -void HashtableTextDump::corrupted(const char *p) { +void HashtableTextDump::corrupted(const char *p, const char* msg) { char info[60]; - sprintf(info, "corrupted at pos %d", (int)(p - _base)); + sprintf(info, "%s. Corrupted at line %d (file pos %d)", msg, _line_no, (int)(p - _base)); quit(info, _filename); } @@ -298,8 +354,9 @@ } else if (_p[0] == '\n') { _p += 1; } else { - corrupted(_p); + corrupted(_p, "Unexpected character"); } + _line_no ++; return true; } @@ -328,26 +385,60 @@ skip_newline(); } +void HashtableTextDump::scan_prefix_type() { + _p ++; + if (strncmp(_p, "SECTION: String", 15) == 0) { + _p += 15; + _prefix_type = StringPrefix; + } else if (strncmp(_p, "SECTION: Symbol", 15) == 0) { + _p += 15; + _prefix_type = SymbolPrefix; + } else { + _prefix_type = Unknown; + } + skip_newline(); +} + +int HashtableTextDump::scan_prefix(int* utf8_length) { + if (*_p == '@') { + scan_prefix_type(); + } + + switch (_prefix_type) { + case SymbolPrefix: + *utf8_length = scan_symbol_prefix(); break; + case StringPrefix: + *utf8_length = scan_string_prefix(); break; + default: + tty->print_cr("Shared input data type: Unknown."); + corrupted(_p, "Unknown data type"); + } + + return _prefix_type; +} -int HashtableTextDump::scan_prefix() { +int HashtableTextDump::scan_string_prefix() { // Expect /[0-9]+: / - int utf8_length = get_num(':'); + int utf8_length; + get_num(':', &utf8_length); if (*_p != ' ') { - corrupted(_p); + corrupted(_p, "Wrong prefix format for string"); } _p++; return utf8_length; } -int HashtableTextDump::scan_prefix2() { +int HashtableTextDump::scan_symbol_prefix() { // Expect /[0-9]+ (-|)[0-9]+: / - int utf8_length = get_num(' '); - if (*_p == '-') { - _p++; + int utf8_length; + get_num(' ', &utf8_length); + if (*_p == '-') { + _p++; } - (void)get_num(':'); + int ref_num; + (void)get_num(':', &ref_num); if (*_p != ' ') { - corrupted(_p); + corrupted(_p, "Wrong prefix format for symbol"); } _p++; return utf8_length; @@ -408,7 +499,7 @@ case 'r': *to++ = '\r'; break; case '\\': *to++ = '\\'; break; default: - ShouldNotReachHere(); + corrupted(_p, "Unsupported character"); } } } --- old/src/share/vm/classfile/compactHashtable.hpp 2015-05-29 14:23:12.917967116 -0400 +++ new/src/share/vm/classfile/compactHashtable.hpp 2015-05-29 14:23:12.017915367 -0400 @@ -28,6 +28,7 @@ #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" @@ -49,7 +50,7 @@ // 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 +// 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. @@ -57,14 +58,14 @@ // 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. +// 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 symbol hash and symbol offset are written to the -// table. For buckets with only one entry, only the symbol offset is written +// 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. @@ -78,6 +79,7 @@ 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; @@ -85,6 +87,9 @@ Symbol *symbol() { return (Symbol*)_literal; } + oop string() { + return (oop)_literal; + } unsigned int hash() { return _hash; } @@ -95,7 +100,7 @@ private: static int number_of_buckets(int num_entries); - const char* _table_name; + int _type; int _num_entries; int _num_buckets; juint* _bucket_sizes; @@ -105,7 +110,7 @@ public: // This is called at dump-time only - CompactHashtableWriter(const char* table_name, int num_entries, CompactHashtableStats* stats); + CompactHashtableWriter(int table_type, int num_entries, CompactHashtableStats* stats); ~CompactHashtableWriter(); int get_required_bytes() { @@ -116,6 +121,10 @@ 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); @@ -123,6 +132,7 @@ public: void dump(char** top, char* end); + const char* table_name(); }; #define REGULAR_BUCKET_TYPE 0 @@ -136,23 +146,23 @@ ///////////////////////////////////////////////////////////////////////////// // -// CompactHashtable is used to stored the CDS archive's symbol table. Used +// 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 symbol table in the shared archive: +// Layout of compact table in the shared archive: // // uintx base_address; -// juint num_symbols; +// 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_symbols | +// | base_address | num_entries | // |---------------------------------| // | num_buckets | bucket_info0 | // |---------------------------------| @@ -177,9 +187,13 @@ // 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 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[]. // @@ -189,19 +203,41 @@ // template 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 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; + inline Symbol* lookup_entry(CompactHashtable* 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* 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: @@ -211,7 +247,14 @@ _table_end_offset = 0; _buckets = 0; } - const char* init(const char *buffer); + 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) { @@ -225,23 +268,22 @@ 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; + // 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 symbol (hash, offset). + // 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) { - juint offset = bucket[1]; - T entry = (T)((void*)(_base_address + offset)); - if (equals(entry, name, len)) { - return entry; + T res = lookup_entry(this, &bucket[1], name, len); + if (res != NULL) { + return res; } } bucket += 2; @@ -253,12 +295,15 @@ // 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). +// 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. // @@ -269,21 +314,29 @@ 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); + void corrupted(const char *p, const char *msg); inline void corrupted_if(bool cond) { if (cond) { - corrupted(_p); + corrupted(_p, NULL); } } @@ -292,7 +345,7 @@ void skip_past(char c); void check_version(const char* ver); - inline int get_num(char delim) { + inline bool get_num(char delim, int *utf8_length) { const char* p = _p; const char* end = _end; int num = 0; @@ -303,18 +356,22 @@ num = num * 10 + (c - '0'); } else if (c == delim) { _p = p; - return num; + *utf8_length = num; + return true; } else { - corrupted(p-1); + // Not [0-9], not 'delim' + return false; } } - corrupted(_end); + corrupted(_end, "Incorrect format"); ShouldNotReachHere(); - return 0; + return false; } - int scan_prefix(); - int scan_prefix2(); + 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); --- old/src/share/vm/classfile/javaClasses.hpp 2015-05-29 14:23:15.722128345 -0400 +++ new/src/share/vm/classfile/javaClasses.hpp 2015-05-29 14:23:14.778074065 -0400 @@ -118,6 +118,10 @@ return hash_offset; } + static void set_value_raw(oop string, typeArrayOop buffer) { + assert(initialized, "Must be initialized"); + string->obj_field_put_raw(value_offset, (oop)buffer); + } static void set_value(oop string, typeArrayOop buffer) { assert(initialized && (value_offset > 0), "Must be initialized"); string->obj_field_put(value_offset, (oop)buffer); @@ -210,6 +214,7 @@ // Debugging static void print(oop java_string, outputStream* st); friend class JavaClasses; + friend class StringTable; }; --- old/src/share/vm/classfile/stringTable.cpp 2015-05-29 14:23:18.374280831 -0400 +++ new/src/share/vm/classfile/stringTable.cpp 2015-05-29 14:23:17.546233223 -0400 @@ -38,6 +38,7 @@ #include "utilities/hashtable.inline.hpp" #include "utilities/macros.hpp" #if INCLUDE_ALL_GCS +#include "gc/g1/g1CollectedHeap.hpp" #include "gc/g1/g1SATBCardTableModRefBS.hpp" #include "gc/g1/g1StringDedup.hpp" #endif @@ -87,19 +88,28 @@ // -------------------------------------------------------------------------- StringTable* StringTable::_the_table = NULL; - +bool StringTable::_ignore_shared_strings = false; bool StringTable::_needs_rehashing = false; volatile int StringTable::_parallel_claimed_idx = 0; +CompactHashtable StringTable::_shared_table; + // Pick hashing algorithm unsigned int StringTable::hash_string(const jchar* s, int len) { return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) : java_lang_String::hash_code(s, len); } -oop StringTable::lookup(int index, jchar* name, - int len, unsigned int hash) { +oop StringTable::lookup_shared(jchar* name, int len) { + // java_lang_String::hash_code() was used to compute hash values in the shared table. Don't + // use the hash value from StringTable::hash_string() as it might use alternate hashcode. + return _shared_table.lookup((const char*)name, + java_lang_String::hash_code(name, len), len); +} + +oop StringTable::lookup_dynamic(int index, jchar* name, + int len, unsigned int hash) { int count = 0; for (HashtableEntry* l = bucket(index); l != NULL; l = l->next()) { count++; @@ -140,7 +150,8 @@ // Since look-up was done lock-free, we need to check if another // thread beat us in the race to insert the symbol. - oop test = lookup(index, name, len, hashValue); // calls lookup(u1*, int) + // No need to lookup the shared table from here since the caller (intern()) already did + oop test = lookup_dynamic(index, name, len, hashValue); // calls lookup(u1*, int) if (test != NULL) { // Entry already added return test; @@ -172,9 +183,14 @@ } oop StringTable::lookup(jchar* name, int len) { + oop string = lookup_shared(name, len); + if (string != NULL) { + return string; + } + unsigned int hash = hash_string(name, len); int index = the_table()->hash_to_index(hash); - oop string = the_table()->lookup(index, name, len, hash); + string = the_table()->lookup_dynamic(index, name, len, hash); ensure_string_alive(string); @@ -184,9 +200,14 @@ oop StringTable::intern(Handle string_or_null, jchar* name, int len, TRAPS) { + oop found_string = lookup_shared(name, len); + if (found_string != NULL) { + return found_string; + } + unsigned int hashValue = hash_string(name, len); int index = the_table()->hash_to_index(hashValue); - oop found_string = the_table()->lookup(index, name, len, hashValue); + found_string = the_table()->lookup_dynamic(index, name, len, hashValue); // Found if (found_string != NULL) { @@ -611,3 +632,131 @@ return 0; } } + +// Sharing +bool StringTable::copy_shared_string(GrowableArray *string_space, + CompactHashtableWriter* ch_table) { +#if INCLUDE_CDS && INCLUDE_ALL_GCS && defined(_LP64) && !defined(_WINDOWS) + assert(UseG1GC, "Only support G1 GC"); + assert(UseCompressedOops && UseCompressedClassPointers, + "Only support UseCompressedOops and UseCompressedClassPointers enabled"); + + Thread* THREAD = Thread::current(); + G1CollectedHeap::heap()->begin_record_alloc_range(); + for (int i = 0; i < the_table()->table_size(); ++i) { + HashtableEntry* bucket = the_table()->bucket(i); + for ( ; bucket != NULL; bucket = bucket->next()) { + oop s = (oop)(bucket->literal()); + unsigned int hash = java_lang_String::hash_code(s); + if (hash == 0) { + continue; + } + + // allocate the new 'value' array first + typeArrayOop v = java_lang_String::value(s); + int v_len = v->size(); + typeArrayOop new_v; + if (G1CollectedHeap::heap()->is_record_alloc_too_large(v_len)) { + continue; // skip the current String. The 'value' array is too large to handle + } else { + new_v = (typeArrayOop)G1CollectedHeap::heap()->record_mem_allocate(v_len); + if (new_v == NULL) { + return false; // allocation failed + } + } + // now allocate the new String object + int s_len = s->size(); + oop new_s = (oop)G1CollectedHeap::heap()->record_mem_allocate(s_len); + if (new_s == NULL) { + return false; + } + + s->identity_hash(); + v->identity_hash(); + + // copy the objects' data + Copy::aligned_disjoint_words((HeapWord*)s, (HeapWord*)new_s, s_len); + Copy::aligned_disjoint_words((HeapWord*)v, (HeapWord*)new_v, v_len); + + // adjust the pointer to the 'value' field in the new String oop. Also pre-compute and set the + // 'hash' field. That avoids "write" to the shared strings at runtime by the deduplication process. + java_lang_String::set_value_raw(new_s, new_v); + if (java_lang_String::hash(new_s) == 0) { + java_lang_String::set_hash(new_s, hash); + } + + // add to the compact table + ch_table->add(hash, new_s); + } + } + + G1CollectedHeap::heap()->end_record_alloc_range(string_space, os::vm_allocation_granularity()); + assert(string_space->length() <= 2, "sanity"); +#endif + return true; +} + +bool StringTable::copy_compact_table(char** top, char *end, GrowableArray *string_space, + size_t* space_size) { +#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS) + if (!(UseG1GC && UseCompressedOops && UseCompressedClassPointers)) { + if (PrintSharedSpaces) { + tty->print_cr("Shared strings are excluded from the archive as UseG1GC, " + "UseCompressedOops and UseCompressedClassPointers are required."); + } + return true; + } + + CompactHashtableWriter ch_table(CompactHashtable::_string_table, + the_table()->number_of_entries(), + &MetaspaceShared::stats()->string); + + // Copy the interned strings into the "string space" within the java heap + if (!copy_shared_string(string_space, &ch_table)) { + return false; + } + + for (int i = 0; i < string_space->length(); i++) { + *space_size += string_space->at(i).byte_size(); + } + + // Now dump the compact table + if (*top + ch_table.get_required_bytes() > end) { + // not enough space left + return false; + } + ch_table.dump(top, end); + *top = (char*)align_pointer_up(*top, sizeof(void*)); + +#endif + return true; +} + +void StringTable::shared_oops_do(OopClosure* f) { +#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS) + _shared_table.oops_do(f); +#endif +} + +const char* StringTable::init_shared_table(FileMapInfo *mapinfo, char *buffer) { +#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS) + if (mapinfo->space_capacity(MetaspaceShared::first_string) == 0) { + // no shared string data + return buffer; + } + + // initialize the shared table + juint *p = (juint*)buffer; + const char* end = _shared_table.init( + CompactHashtable::_string_table, (char*)p); + const char* aligned_end = (const char*)align_pointer_up(end, sizeof(void*)); + + if (_ignore_shared_strings) { + _shared_table.reset(); + } + + return aligned_end; +#endif + + return buffer; +} --- old/src/share/vm/classfile/stringTable.hpp 2015-05-29 14:23:21.110438149 -0400 +++ new/src/share/vm/classfile/stringTable.hpp 2015-05-29 14:23:20.122381340 -0400 @@ -28,6 +28,10 @@ #include "memory/allocation.inline.hpp" #include "utilities/hashtable.hpp" +template class CompactHashtable; +class CompactHashtableWriter; +class FileMapInfo; + class StringTable : public RehashableHashtable { friend class VMStructs; friend class Symbol; @@ -36,17 +40,22 @@ // The string table static StringTable* _the_table; + // Shared string table + static CompactHashtable _shared_table; + static bool _ignore_shared_strings; + // Set if one bucket is out of balance due to hash algorithm deficiency static bool _needs_rehashing; // Claimed high water mark for parallel chunked scanning static volatile int _parallel_claimed_idx; - + static oop intern(Handle string_or_null, jchar* chars, int length, TRAPS); oop basic_add(int index, Handle string_or_null, jchar* name, int len, unsigned int hashValue, TRAPS); - oop lookup(int index, jchar* chars, int length, unsigned int hashValue); + oop lookup_dynamic(int index, jchar* chars, int length, unsigned int hashValue); + static oop lookup_shared(jchar* name, int len); // Apply the give oop closure to the entries to the buckets // in the range [start_idx, end_idx). @@ -141,12 +150,14 @@ static int verify_and_compare_entries(); // Sharing - static void copy_buckets(char** top, char*end) { - the_table()->Hashtable::copy_buckets(top, end); - } - static void copy_table(char** top, char*end) { - the_table()->Hashtable::copy_table(top, end); - } + static void ignore_shared_strings(bool v) { _ignore_shared_strings = v; } + static bool shared_string_ignored() { return _ignore_shared_strings; } + static void shared_oops_do(OopClosure* f); + static bool copy_shared_string(GrowableArray *string_space, + CompactHashtableWriter* ch_table); + static bool copy_compact_table(char** top, char* end, GrowableArray *string_space, + size_t* space_size); + static const char* init_shared_table(FileMapInfo *mapinfo, char* buffer); static void reverse() { the_table()->Hashtable::reverse(); } --- old/src/share/vm/classfile/symbolTable.cpp 2015-05-29 14:23:23.798592704 -0400 +++ new/src/share/vm/classfile/symbolTable.cpp 2015-05-29 14:23:22.970545095 -0400 @@ -539,7 +539,8 @@ bool SymbolTable::copy_compact_table(char** top, char*end) { #if INCLUDE_CDS - CompactHashtableWriter ch_table("symbol", the_table()->number_of_entries(), + CompactHashtableWriter ch_table(CompactHashtable::_symbol_table, + the_table()->number_of_entries(), &MetaspaceShared::stats()->symbol); if (*top + ch_table.get_required_bytes() > end) { // not enough space left @@ -556,7 +557,6 @@ } } - char* old_top = *top; ch_table.dump(top, end); *top = (char*)align_pointer_up(*top, sizeof(void*)); @@ -565,7 +565,8 @@ } const char* SymbolTable::init_shared_table(const char* buffer) { - const char* end = _shared_table.init(buffer); + const char* end = _shared_table.init( + CompactHashtable::_symbol_table, buffer); return (const char*)align_pointer_up(end, sizeof(void*)); } --- old/src/share/vm/gc/g1/g1StringDedupThread.cpp 2015-05-29 14:23:26.474746571 -0400 +++ new/src/share/vm/gc/g1/g1StringDedupThread.cpp 2015-05-29 14:23:25.634698271 -0400 @@ -23,12 +23,14 @@ */ #include "precompiled.hpp" +#include "classfile/stringTable.hpp" #include "gc/g1/g1Log.hpp" #include "gc/g1/g1StringDedup.hpp" #include "gc/g1/g1StringDedupQueue.hpp" #include "gc/g1/g1StringDedupTable.hpp" #include "gc/g1/g1StringDedupThread.hpp" #include "gc/g1/suspendibleThreadSet.hpp" +#include "oops/oop.inline.hpp" #include "runtime/atomic.inline.hpp" G1StringDedupThread* G1StringDedupThread::_thread = NULL; @@ -55,11 +57,31 @@ return _thread; } +class G1StringDedupSharedClosure: public OopClosure { + private: + G1StringDedupStat& _stat; + + public: + G1StringDedupSharedClosure(G1StringDedupStat& stat) : _stat(stat) {} + + virtual void do_oop(oop* p) { ShouldNotReachHere(); } + virtual void do_oop(narrowOop* p) { + oop java_string = oopDesc::load_decode_heap_oop(p); + G1StringDedupTable::deduplicate(java_string, _stat); + } +}; + +void G1StringDedupThread::deduplicate_shared_strings(G1StringDedupStat& stat) { + G1StringDedupSharedClosure sharedStringDedup(stat); + StringTable::shared_oops_do(&sharedStringDedup); +} + void G1StringDedupThread::run() { G1StringDedupStat total_stat; initialize_in_thread(); wait_for_universe_init(); + deduplicate_shared_strings(total_stat); // Main loop for (;;) { --- old/src/share/vm/gc/g1/g1StringDedupThread.hpp 2015-05-29 14:23:29.198903195 -0400 +++ new/src/share/vm/gc/g1/g1StringDedupThread.hpp 2015-05-29 14:23:28.366855357 -0400 @@ -52,6 +52,8 @@ static G1StringDedupThread* thread(); virtual void run(); + + void deduplicate_shared_strings(G1StringDedupStat& stat); }; #endif // SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP --- old/src/share/vm/memory/filemap.cpp 2015-05-29 14:23:31.887057752 -0400 +++ new/src/share/vm/memory/filemap.cpp 2015-05-29 14:23:30.987006002 -0400 @@ -28,6 +28,9 @@ #include "classfile/symbolTable.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/altHashing.hpp" +#if INCLUDE_ALL_GCS +#include "gc/g1/g1CollectedHeap.hpp" +#endif #include "memory/filemap.hpp" #include "memory/metadataFactory.hpp" #include "memory/oopFactory.hpp" @@ -165,6 +168,9 @@ _version = _current_version; _alignment = alignment; _obj_alignment = ObjectAlignmentInBytes; + _narrow_oop_mode = Universe::narrow_oop_mode(); + _narrow_oop_shift = Universe::narrow_oop_shift(); + _max_heap_size = MaxHeapSize; _classpath_entry_table_size = mapinfo->_classpath_entry_table_size; _classpath_entry_table = mapinfo->_classpath_entry_table; _classpath_entry_size = mapinfo->_classpath_entry_size; @@ -440,7 +446,16 @@ } else { si->_file_offset = _file_offset; } - si->_base = base; + if (MetaspaceShared::is_string_region(region)) { + assert((base - (char*)Universe::narrow_oop_base()) % HeapWordSize == 0, "Sanity"); + if (base != NULL) { + si->_addr._offset = (intx)oopDesc::encode_heap_oop_not_null((oop)base); + } else { + si->_addr._offset = 0; + } + } else { + si->_addr._base = base; + } si->_used = size; si->_capacity = capacity; si->_read_only = read_only; @@ -449,6 +464,38 @@ write_bytes_aligned(base, (int)size); } +// Write the string space. The string space contains one or multiple GC(G1) regions. +// When the total string space size is smaller than one GC region of the dump time, +// only one string region is used for shared strings. +// +// If the total string space size is bigger than one GC region, there would be more +// than one GC regions allocated for shared strings. The first/bottom GC region might +// be a partial GC region with the empty portion at the higher address within that region. +// The non-empty portion of the first region is written into the archive as one string +// region. The rest are consecutive full GC regions if they exist, which can be written +// out in one chunk as another string region. +void FileMapInfo::write_string_regions(GrowableArray *regions) { + for (int i = MetaspaceShared::first_string; + i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { + char* start = NULL; + size_t size = 0; + if (regions->is_nonempty()) { + if (i == MetaspaceShared::first_string) { + MemRegion first = regions->first(); + start = (char*)first.start(); + size = first.byte_size(); + } else { + int len = regions->length(); + if (len > 1) { + start = (char*)regions->at(1).start(); + size = (char*)regions->at(len - 1).end() - start; + } + } + } + write_region(i, start, size, size, false, false); + } +} + // Dump bytes to file -- at the current file position. @@ -513,7 +560,8 @@ // JVM/TI RedefineClasses() support: // Remap the shared readonly space to shared readwrite, private. bool FileMapInfo::remap_shared_readonly_as_readwrite() { - struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; + int idx = 0; + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[idx]; if (!si->_read_only) { // the space is already readwrite so we are done return true; @@ -523,15 +571,16 @@ if (!open_for_read()) { return false; } + char *addr = _header->region_addr(idx); char *base = os::remap_memory(_fd, _full_path, si->_file_offset, - si->_base, size, false /* !read_only */, + addr, size, false /* !read_only */, si->_allow_exec); close(); if (base == NULL) { fail_continue("Unable to remap shared readonly space (errno=%d).", errno); return false; } - if (base != si->_base) { + if (base != addr) { fail_continue("Unable to remap shared readonly space at required address."); return false; } @@ -542,7 +591,7 @@ // Map the whole region at once, assumed to be allocated contiguously. ReservedSpace FileMapInfo::reserve_shared_memory() { struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0]; - char* requested_addr = si->_base; + char* requested_addr = _header->region_addr(0); size_t size = FileMapInfo::shared_spaces_size(); @@ -560,20 +609,22 @@ } // Memory map a region in the address space. -static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode"}; +static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode", + "String1", "String2"}; char* FileMapInfo::map_region(int i) { + assert(!MetaspaceShared::is_string_region(i), "sanity"); struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; size_t used = si->_used; size_t alignment = os::vm_allocation_granularity(); size_t size = align_size_up(used, alignment); - char *requested_addr = si->_base; + char *requested_addr = _header->region_addr(i); // map the contents of the CDS archive in this memory char *base = os::map_memory(_fd, _full_path, si->_file_offset, requested_addr, size, si->_read_only, si->_allow_exec); - if (base == NULL || base != si->_base) { + if (base == NULL || base != requested_addr) { fail_continue("Unable to map %s shared space at required address.", shared_region_name[i]); return NULL; } @@ -582,15 +633,116 @@ // in method FileMapInfo::reserve_shared_memory(), which is not called on Windows. MemTracker::record_virtual_memory_type((address)base, mtClassShared); #endif + return base; } +MemRegion *string_ranges = NULL; +int num_ranges = 0; +bool FileMapInfo::map_string_regions() { +#if INCLUDE_ALL_GCS + if (UseG1GC && UseCompressedOops && UseCompressedClassPointers) { + if (narrow_oop_mode() == Universe::narrow_oop_mode() && + narrow_oop_shift() == Universe::narrow_oop_shift()) { + string_ranges = new MemRegion[MetaspaceShared::max_strings]; + struct FileMapInfo::FileMapHeader::space_info* si; + + for (int i = MetaspaceShared::first_string; + i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { + si = &_header->_space[i]; + size_t used = si->_used; + if (used > 0) { + size_t size = used; + char* requested_addr = (char*)((void*)oopDesc::decode_heap_oop_not_null( + (narrowOop)si->_addr._offset)); + string_ranges[num_ranges] = MemRegion((HeapWord*)requested_addr, size / HeapWordSize); + num_ranges ++; + } + } + + // Check that ranges are within the java heap + if (!G1CollectedHeap::heap()->check_archive_addresses(string_ranges, num_ranges)) { + fail_continue("Unable to allocate shared string space: range is not " + "within java heap."); + return false; + } + + // allocate from java heap + if (!G1CollectedHeap::heap()->alloc_archive_regions(string_ranges, num_ranges)) { + fail_continue("Unable to allocate shared string space: range is " + "already in use."); + return false; + } + + // Map the string data. No need to call MemTracker::record_virtual_memory_type() + // for mapped string regions as they are part of the reserved java heap, which + // is already recorded. + for (int i = 0; i < num_ranges; i++) { + si = &_header->_space[MetaspaceShared::first_string + i]; + char* addr = (char*)string_ranges[i].start(); + char* base = os::map_memory(_fd, _full_path, si->_file_offset, + addr, string_ranges[i].byte_size(), si->_read_only, + si->_allow_exec); + if (base == NULL || base != addr) { + fail_continue("Unable to map shared string space at required address."); + return false; + } + } + return true; // the shared string data is mapped successfuly + } else { + // narrow oop encoding differ, the shared string data are not used + if (PrintSharedSpaces && _header->_space[MetaspaceShared::first_string]._used > 0) { + tty->print_cr("Shared string data from the CDS archive is being ignored. " + "The current CompressedOops encoding differs from that archived " + "due to heap size change. The archive was dumped using max heap " + "size %dM.", max_heap_size() >> 20); + } + } + } else { + if (PrintSharedSpaces && _header->_space[MetaspaceShared::first_string]._used > 0) { + tty->print_cr("Shared string data from the CDS archive is being ignored. UseG1GC, " + "UseCompressedOops and UseCompressedClassPointers are required."); + } + } + + // if we get here, the shared string data is not mapped + assert(string_ranges == NULL && num_ranges == 0, "sanity"); + StringTable::ignore_shared_strings(true); +#endif + return true; +} + +bool FileMapInfo::verify_string_regions() { + for (int i = MetaspaceShared::first_string; + i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { + if (!verify_region_checksum(i)) { + return false; + } + } + return true; +} + +void FileMapInfo::fixup_string_regions() { + if (string_ranges != NULL) { + G1CollectedHeap::heap()->fill_archive_regions(string_ranges, num_ranges); + } +} + bool FileMapInfo::verify_region_checksum(int i) { if (!VerifySharedSpaces) { return true; } - const char* buf = _header->_space[i]._base; + + const char* buf; size_t sz = _header->_space[i]._used; + + if (sz == 0) { + return true; // no data + } + if (MetaspaceShared::is_string_region(i) && StringTable::shared_string_ignored()) { + return true; // shared string data are not mapped + } + buf = _header->region_addr(i); int crc = ClassLoader::crc32(0, buf, (jint)sz); if (crc != _header->_space[i]._crc) { fail_continue("Checksum verification failed."); @@ -602,14 +754,37 @@ // Unmap a memory region in the address space. void FileMapInfo::unmap_region(int i) { + assert(!MetaspaceShared::is_string_region(i), "sanity"); struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; size_t used = si->_used; size_t size = align_size_up(used, os::vm_allocation_granularity()); - if (!os::unmap_memory(si->_base, size)) { + char* addr; + + if (used == 0) { + return; + } + + addr = _header->region_addr(i); + if (!os::unmap_memory(addr, size)) { fail_stop("Unable to unmap shared space."); } } +void FileMapInfo::unmap_string_regions() { + for (int i = MetaspaceShared::first_string; + i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { + struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; + size_t used = si->_used; + if (used > 0) { + size_t size = align_size_up(used, os::vm_allocation_granularity()); + char* addr = (char*)((void*)oopDesc::decode_heap_oop_not_null( + (narrowOop)si->_addr._offset)); + if (!os::unmap_memory(addr, size)) { + fail_stop("Unable to unmap shared space."); + } + } + } +} void FileMapInfo::assert_mark(bool check) { if (!check) { @@ -658,6 +833,15 @@ return true; } +char* FileMapInfo::FileMapHeader::region_addr(int idx) { + if (MetaspaceShared::is_string_region(idx)) { + return (char*)((void*)oopDesc::decode_heap_oop_not_null( + (narrowOop)_space[idx]._addr._offset)); + } else { + return _space[idx]._addr._base; + } +} + int FileMapInfo::FileMapHeader::compute_crc() { char* header = data(); // start computing from the field after _crc @@ -729,8 +913,12 @@ // True if the p is within the mapped shared space, otherwise, false. bool FileMapInfo::is_in_shared_space(const void* p) { for (int i = 0; i < MetaspaceShared::n_regions; i++) { - if (p >= _header->_space[i]._base && - p < _header->_space[i]._base + _header->_space[i]._used) { + char *base; + if (MetaspaceShared::is_string_region(i) && _header->_space[i]._used == 0) { + continue; + } + base = _header->region_addr(i); + if (p >= base && p < base + _header->_space[i]._used) { return true; } } @@ -742,9 +930,10 @@ gclog_or_tty->print_cr("Shared Spaces:"); for (int i = 0; i < MetaspaceShared::n_regions; i++) { struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; + char *base = _header->region_addr(i); gclog_or_tty->print(" %s " INTPTR_FORMAT "-" INTPTR_FORMAT, shared_region_name[i], - si->_base, si->_base + si->_used); + base, base + si->_used); } } @@ -753,12 +942,14 @@ FileMapInfo *map_info = FileMapInfo::current_info(); if (map_info) { map_info->fail_continue("%s", msg); - for (int i = 0; i < MetaspaceShared::n_regions; i++) { - if (map_info->_header->_space[i]._base != NULL) { + for (int i = 0; i < MetaspaceShared::num_non_strings; i++) { + char *addr = map_info->_header->region_addr(i); + if (addr != NULL && !MetaspaceShared::is_string_region(i)) { map_info->unmap_region(i); - map_info->_header->_space[i]._base = NULL; + map_info->_header->_space[i]._addr._base = NULL; } } + map_info->unmap_string_regions(); } else if (DumpSharedSpaces) { fail_stop("%s", msg); } --- old/src/share/vm/memory/filemap.hpp 2015-05-29 14:23:35.419260834 -0400 +++ new/src/share/vm/memory/filemap.hpp 2015-05-29 14:23:34.491207476 -0400 @@ -94,11 +94,18 @@ int _version; // (from enum, above.) size_t _alignment; // how shared archive should be aligned int _obj_alignment; // value of ObjectAlignmentInBytes + int _narrow_oop_shift; // compressed oop encoding shift + uintx _max_heap_size; // java max heap size during dumping + Universe::NARROW_OOP_MODE _narrow_oop_mode; struct space_info { int _crc; // crc checksum of the current space size_t _file_offset; // sizeof(this) rounded to vm page size - char* _base; // copy-on-write base address + union { + char* _base; // copy-on-write base address + intx _offset; // offset from the compressed oop encoding base, only used + // by string space + } _addr; size_t _capacity; // for validity checking size_t _used; // for setting space top on read bool _read_only; // read only space? @@ -138,6 +145,8 @@ size_t _classpath_entry_size; SharedClassPathEntry* _classpath_entry_table; + char* region_addr(int idx); + virtual bool validate(); virtual void populate(FileMapInfo* info, size_t alignment); int compute_crc(); @@ -166,8 +175,10 @@ void invalidate(); int version() { return _header->_version; } size_t alignment() { return _header->_alignment; } + Universe::NARROW_OOP_MODE narrow_oop_mode() { return _header->_narrow_oop_mode; } + int narrow_oop_shift() { return _header->_narrow_oop_shift; } + uintx max_heap_size() { return _header->_max_heap_size; } size_t space_capacity(int i) { return _header->_space[i]._capacity; } - char* region_base(int i) { return _header->_space[i]._base; } struct FileMapHeader* header() { return _header; } static FileMapInfo* current_info() { @@ -185,10 +196,15 @@ void write_space(int i, Metaspace* space, bool read_only); void write_region(int region, char* base, size_t size, size_t capacity, bool read_only, bool allow_exec); + void write_string_regions(GrowableArray *regions); void write_bytes(const void* buffer, int count); void write_bytes_aligned(const void* buffer, int count); char* map_region(int i); + bool map_string_regions(); + bool verify_string_regions(); + void fixup_string_regions(); void unmap_region(int i); + void unmap_string_regions(); bool verify_region_checksum(int i); void close(); bool is_open() { return _file_open; } --- old/src/share/vm/memory/metaspace.cpp 2015-05-29 14:23:38.027410786 -0400 +++ new/src/share/vm/memory/metaspace.cpp 2015-05-29 14:23:37.151360420 -0400 @@ -3259,7 +3259,7 @@ // Map in spaces now also if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) { cds_total = FileMapInfo::shared_spaces_size(); - cds_address = (address)mapinfo->region_base(0); + cds_address = (address)mapinfo->header()->region_addr(0); } else { assert(!mapinfo->is_open() && !UseSharedSpaces, "archive file not closed or shared spaces not disabled."); --- old/src/share/vm/memory/metaspaceShared.cpp 2015-05-29 14:23:40.703564655 -0400 +++ new/src/share/vm/memory/metaspaceShared.cpp 2015-05-29 14:23:39.859516125 -0400 @@ -422,6 +422,8 @@ GrowableArray *_class_promote_order; VirtualSpace _md_vs; VirtualSpace _mc_vs; + CompactHashtableWriter* _string_cht; + GrowableArray *_string_regions; public: VM_PopulateDumpSharedSpace(ClassLoaderData* loader_data, @@ -540,7 +542,7 @@ NOT_PRODUCT(SystemDictionary::verify();) - // Copy the the symbol table, and the system dictionary to the shared + // Copy the the symbol table, string table, and the system dictionary to the shared // space in usable form. Copy the hashtable // buckets first [read-write], then copy the linked lists of entries // [read-only]. @@ -548,6 +550,15 @@ NOT_PRODUCT(SymbolTable::verify()); handle_misc_data_space_failure(SymbolTable::copy_compact_table(&md_top, md_end)); + size_t ss_bytes = 0; + char* ss_low; + // The string space has maximum two regions. See FileMapInfo::write_string_regions() for details. + _string_regions = new GrowableArray(2); + NOT_PRODUCT(StringTable::verify()); + handle_misc_data_space_failure(StringTable::copy_compact_table(&md_top, md_end, _string_regions, + &ss_bytes)); + ss_low = _string_regions->is_empty() ? NULL : (char*)_string_regions->first().start(); + SystemDictionary::reverse(); SystemDictionary::copy_buckets(&md_top, md_end); @@ -576,7 +587,8 @@ const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType); const size_t md_alloced = md_end-md_low; const size_t mc_alloced = mc_end-mc_low; - const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced; + const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced + + ss_bytes; // Occupied size of each space. const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType); @@ -585,11 +597,12 @@ const size_t mc_bytes = size_t(mc_top - mc_low); // Percent of total size - const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes; + const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes; const double ro_t_perc = ro_bytes / double(total_bytes) * 100.0; const double rw_t_perc = rw_bytes / double(total_bytes) * 100.0; const double md_t_perc = md_bytes / double(total_bytes) * 100.0; const double mc_t_perc = mc_bytes / double(total_bytes) * 100.0; + const double ss_t_perc = ss_bytes / double(total_bytes) * 100.0; // Percent of fullness of each space const double ro_u_perc = ro_bytes / double(ro_alloced) * 100.0; @@ -602,6 +615,7 @@ tty->print_cr(fmt_space, "rw", rw_bytes, rw_t_perc, rw_alloced, rw_u_perc, rw_space->bottom()); tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, md_low); tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, mc_low); + tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes, 100.0, ss_low); tty->print_cr("total : %9d [100.0%% of total] out of %9d bytes [%4.1f%% used]", total_bytes, total_alloced, total_u_perc); @@ -631,6 +645,7 @@ pointer_delta(mc_top, _mc_vs.low(), sizeof(char)), SharedMiscCodeSize, true, true); + mapinfo->write_string_regions(_string_regions); // Pass 2 - write data. mapinfo->open_for_write(); @@ -646,6 +661,8 @@ pointer_delta(mc_top, _mc_vs.low(), sizeof(char)), SharedMiscCodeSize, true, true); + mapinfo->write_string_regions(_string_regions); + mapinfo->close(); memmove(vtbl_list, saved_vtbl, vtbl_list_size * sizeof(void*)); @@ -942,6 +959,11 @@ return UseSharedSpaces && FileMapInfo::current_info()->is_in_shared_space(p); } +bool MetaspaceShared::is_string_region(int idx) { + return (idx >= MetaspaceShared::first_string && + idx < MetaspaceShared::first_string + MetaspaceShared::max_strings); +} + void MetaspaceShared::print_shared_spaces() { if (UseSharedSpaces) { FileMapInfo::current_info()->print_shared_spaces(); @@ -972,13 +994,15 @@ // Map each shared region if ((_ro_base = mapinfo->map_region(ro)) != NULL && - mapinfo->verify_region_checksum(ro) && + mapinfo->verify_region_checksum(ro) && (_rw_base = mapinfo->map_region(rw)) != NULL && - mapinfo->verify_region_checksum(rw) && + mapinfo->verify_region_checksum(rw) && (_md_base = mapinfo->map_region(md)) != NULL && - mapinfo->verify_region_checksum(md) && + mapinfo->verify_region_checksum(md) && (_mc_base = mapinfo->map_region(mc)) != NULL && - mapinfo->verify_region_checksum(mc) && + mapinfo->verify_region_checksum(mc) && + mapinfo->map_string_regions() && + mapinfo->verify_string_regions() && (image_alignment == (size_t)max_alignment()) && mapinfo->validate_classpath_entry_table()) { // Success (no need to do anything) @@ -990,6 +1014,7 @@ if (_rw_base != NULL) mapinfo->unmap_region(rw); if (_md_base != NULL) mapinfo->unmap_region(md); if (_mc_base != NULL) mapinfo->unmap_region(mc); + mapinfo->unmap_string_regions(); #ifndef _WINDOWS // Release the entire mapped region shared_rs.release(); @@ -1011,7 +1036,7 @@ void MetaspaceShared::initialize_shared_spaces() { FileMapInfo *mapinfo = FileMapInfo::current_info(); - char* buffer = mapinfo->region_base(md); + char* buffer = mapinfo->header()->region_addr(md); // Skip over (reserve space for) a list of addresses of C++ vtables // for Klass objects. They get filled in later. @@ -1027,13 +1052,16 @@ buffer += sizeof(intptr_t); buffer += vtable_size; - // Create the shared symbol table using the bucket array at this spot in the + // Create the shared symbol table using the compact table at this spot in the // misc data space. (Todo: move this to read-only space. Currently // this is mapped copy-on-write but will never be written into). buffer = (char*)SymbolTable::init_shared_table(buffer); SymbolTable::create_table(); + // Create the shared string table using the compact table + buffer = (char*)StringTable::init_shared_table(mapinfo, buffer); + // Create the shared dictionary using the bucket array at this spot in // the misc data space. Since the shared dictionary table is never // modified, this region (of mapped pages) will be (effectively, if @@ -1100,6 +1128,11 @@ } } +void MetaspaceShared::fixup_shared_string_regions() { + FileMapInfo *mapinfo = FileMapInfo::current_info(); + mapinfo->fixup_string_regions(); +} + // JVM/TI RedefineClasses() support: bool MetaspaceShared::remap_shared_readonly_as_readwrite() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); --- old/src/share/vm/memory/metaspaceShared.hpp 2015-05-29 14:23:43.511726105 -0400 +++ new/src/share/vm/memory/metaspaceShared.hpp 2015-05-29 14:23:42.591673209 -0400 @@ -53,6 +53,7 @@ memset(this, 0, sizeof(*this)); } CompactHashtableStats symbol; + CompactHashtableStats string; }; // Class Data Sharing Support @@ -90,7 +91,10 @@ rw = 1, // read-write shared space in the heap md = 2, // miscellaneous data for initializing tables, etc. mc = 3, // miscellaneous code - vtable replacement. - n_regions = 4 + max_strings = 2, // max number of string regions in string space + num_non_strings = 4, // number of non-string regions + first_string = num_non_strings, // index of first string region + n_regions = max_strings + num_non_strings // total number of regions }; // Accessor functions to save shared space created for metadata, which has @@ -124,10 +128,13 @@ } static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false); static void initialize_shared_spaces() NOT_CDS_RETURN; + static void fixup_shared_string_regions() NOT_CDS_RETURN; // Return true if given address is in the mapped shared space. static bool is_in_shared_space(const void* p) NOT_CDS_RETURN_(false); + static bool is_string_region(int idx) NOT_CDS_RETURN_(false); + static void generate_vtable_methods(void** vtbl_list, void** vtable, char** md_top, char* md_end, --- old/src/share/vm/memory/universe.cpp 2015-05-29 14:23:46.143877439 -0400 +++ new/src/share/vm/memory/universe.cpp 2015-05-29 14:23:45.255826380 -0400 @@ -311,6 +311,7 @@ SystemDictionary::Cloneable_klass(), "u3"); assert(_the_array_interfaces_array->at(1) == SystemDictionary::Serializable_klass(), "u3"); + MetaspaceShared::fixup_shared_string_regions(); } else { // Set up shared interfaces array. (Do this before supers are set up.) _the_array_interfaces_array->at_put(0, SystemDictionary::Cloneable_klass()); --- old/src/share/vm/prims/whitebox.cpp 2015-05-29 14:23:48.852033144 -0400 +++ new/src/share/vm/prims/whitebox.cpp 2015-05-29 14:23:47.995983923 -0400 @@ -31,6 +31,7 @@ #include "code/codeCache.hpp" #include "jvmtifiles/jvmtiEnv.hpp" #include "memory/metadataFactory.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/universe.hpp" #include "oops/oop.inline.hpp" #include "prims/wbtestmethods/parserTests.hpp" @@ -1179,6 +1180,11 @@ return NULL; WB_END +WB_ENTRY(jboolean, WB_IsShared(JNIEnv* env, jobject wb, jobject obj)) + oop obj_oop = JNIHandles::resolve(obj); + return MetaspaceShared::is_in_shared_space((void*)obj_oop); +WB_END + //Some convenience methods to deal with objects from java int WhiteBox::offset_for_field(const char* field_name, oop object, Symbol* signature_symbol) { @@ -1397,6 +1403,7 @@ {CC"getMethodStringOption", CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)Ljava/lang/String;", (void*)&WB_GetMethodStringOption}, + {CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared }, }; #undef CC --- old/src/share/vm/runtime/synchronizer.cpp 2015-05-29 14:23:55.140394683 -0400 +++ new/src/share/vm/runtime/synchronizer.cpp 2015-05-29 14:23:53.496300159 -0400 @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "classfile/vmSymbols.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/padded.hpp" #include "memory/resourceArea.hpp" #include "oops/markOop.hpp" @@ -638,11 +639,11 @@ // hashCode() is a heap mutator ... // Relaxing assertion for bug 6320749. - assert(Universe::verify_in_progress() || + assert(Universe::verify_in_progress() || DumpSharedSpaces || !SafepointSynchronize::is_at_safepoint(), "invariant"); - assert(Universe::verify_in_progress() || + assert(Universe::verify_in_progress() || DumpSharedSpaces || Self->is_Java_thread() , "invariant"); - assert(Universe::verify_in_progress() || + assert(Universe::verify_in_progress() || DumpSharedSpaces || ((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant"); ObjectMonitor* monitor = NULL; --- old/src/share/vm/services/virtualMemoryTracker.cpp 2015-05-29 14:24:00.088679177 -0400 +++ new/src/share/vm/services/virtualMemoryTracker.cpp 2015-05-29 14:23:58.440584424 -0400 @@ -347,6 +347,13 @@ return true; } + // Mapped CDS string region. + // The string region(s) is part of the java heap. + if (reserved_rgn->flag() == mtJavaHeap) { + assert(reserved_rgn->contain_region(base_addr, size), "Reserved heap region should contain this mapping region"); + return true; + } + ShouldNotReachHere(); return false; }