--- old/src/hotspot/share/classfile/stringTable.cpp 2018-06-04 17:06:49.666460237 +0200 +++ new/src/hotspot/share/classfile/stringTable.cpp 2018-06-04 17:06:49.240445946 +0200 @@ -55,8 +55,6 @@ // We prefer short chains of avg 2 #define PREF_AVG_LIST_LEN 2 -// We start with same old size, consider to reduce this -#define START_SIZE 16 // 2^24 is max size #define END_SIZE 24 // If a chain gets to 32 something might be wrong @@ -114,7 +112,7 @@ } }; -class StringTableLookupJchar { +class StringTableLookupJchar : StackObj { private: Thread* _thread; uintx _hash; @@ -178,13 +176,22 @@ } }; +static size_t nearest_pow_2(uintx val) { + size_t ret; + for (ret = 1; ((size_t)1 << ret) < val; ++ret); + return ret; +} + StringTable::StringTable() : _local_table(NULL), _current_size(0), _has_work(0), _needs_rehashing(false), _weak_handles(NULL), _items(0), _uncleaned_items(0) { _weak_handles = new OopStorage("StringTable weak", StringTableWeakAlloc_lock, StringTableWeakActive_lock); - _local_table = new StringTableHash(START_SIZE, END_SIZE, REHASH_LEN); - _current_size = ((size_t)1) << START_SIZE; + size_t start_size_log_2 = nearest_pow_2(StringTableSize); + _current_size = ((size_t)1) << start_size_log_2; + log_trace(stringtable)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")", + _current_size, start_size_log_2); + _local_table = new StringTableHash(start_size_log_2, END_SIZE, REHASH_LEN); } size_t StringTable::item_added() { @@ -212,6 +219,18 @@ return (_uncleaned_items*1.0)/_current_size; } +size_t StringTable::table_size(Thread* thread) { + return ((size_t)(1)) << _local_table->get_size_log2(thread != NULL ? thread + : Thread::current()); +} + +void StringTable::trigger_concurrent_work() { + MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); + the_table()->_has_work = true; + Service_lock->notify_all(); +} + +// Probing oop StringTable::lookup(Symbol* symbol) { ResourceMark rm; int length; @@ -228,7 +247,7 @@ if (StringTable::_alt_hash) { hash = hash_string(name, len, true); } - return StringTable::the_table()->do_lookup( name, len, hash); + return StringTable::the_table()->do_lookup(name, len, hash); } class StringTableGet : public StackObj { @@ -258,6 +277,53 @@ return stg.get_res_oop(); } +// Interning +oop StringTable::intern(Symbol* symbol, TRAPS) { + if (symbol == NULL) return NULL; + ResourceMark rm(THREAD); + int length; + jchar* chars = symbol->as_unicode(length); + Handle string; + oop result = intern(string, chars, length, CHECK_NULL); + return result; +} + +oop StringTable::intern(oop string, TRAPS) { + if (string == NULL) return NULL; + ResourceMark rm(THREAD); + int length; + Handle h_string (THREAD, string); + jchar* chars = java_lang_String::as_unicode_string(string, length, + CHECK_NULL); + oop result = intern(h_string, chars, length, CHECK_NULL); + return result; +} + +oop StringTable::intern(const char* utf8_string, TRAPS) { + if (utf8_string == NULL) return NULL; + ResourceMark rm(THREAD); + int length = UTF8::unicode_length(utf8_string); + jchar* chars = NEW_RESOURCE_ARRAY(jchar, length); + UTF8::convert_to_unicode(utf8_string, chars, length); + Handle string; + oop result = intern(string, chars, length, CHECK_NULL); + return result; +} + +oop StringTable::intern(Handle string_or_null_h, jchar* name, int len, TRAPS) { + // shared table always uses java_lang_String::hash_code + unsigned int hash = java_lang_String::hash_code(name, len); + oop found_string = StringTable::the_table()->lookup_shared(name, len, hash); + if (found_string != NULL) { + return found_string; + } + if (StringTable::_alt_hash) { + hash = hash_string(name, len, true); + } + return StringTable::the_table()->do_intern(string_or_null_h, name, len, + hash, CHECK_NULL); +} + class StringTableCreateEntry : public StackObj { private: Thread* _thread; @@ -282,20 +348,6 @@ } }; -oop StringTable::intern(Handle string_or_null_h, jchar* name, int len, TRAPS) { - // shared table always uses java_lang_String::hash_code - unsigned int hash = java_lang_String::hash_code(name, len); - oop found_string = StringTable::the_table()->lookup_shared(name, len, hash); - if (found_string != NULL) { - return found_string; - } - if (StringTable::_alt_hash) { - hash = hash_string(name, len, true); - } - return StringTable::the_table()->do_intern(string_or_null_h, name, len, - hash, CHECK_NULL); -} - oop StringTable::do_intern(Handle string_or_null_h, jchar* name, int len, uintx hash, TRAPS) { HandleMark hm(THREAD); // cleanup strings created @@ -326,43 +378,7 @@ return stc.get_return(); } -oop StringTable::intern(Symbol* symbol, TRAPS) { - if (symbol == NULL) return NULL; - ResourceMark rm(THREAD); - int length; - jchar* chars = symbol->as_unicode(length); - Handle string; - oop result = intern(string, chars, length, CHECK_NULL); - return result; -} - -oop StringTable::intern(oop string, TRAPS) { - if (string == NULL) return NULL; - ResourceMark rm(THREAD); - int length; - Handle h_string (THREAD, string); - jchar* chars = java_lang_String::as_unicode_string(string, length, - CHECK_NULL); - oop result = intern(h_string, chars, length, CHECK_NULL); - return result; -} - -oop StringTable::intern(const char* utf8_string, TRAPS) { - if (utf8_string == NULL) return NULL; - ResourceMark rm(THREAD); - int length = UTF8::unicode_length(utf8_string); - jchar* chars = NEW_RESOURCE_ARRAY(jchar, length); - UTF8::convert_to_unicode(utf8_string, chars, length); - Handle string; - oop result = intern(string, chars, length, CHECK_NULL); - return result; -} - -size_t StringTable::table_size(Thread* thread) { - return ((size_t)(1)) << _local_table->get_size_log2(thread != NULL ? thread - : Thread::current()); -} - +// GC support class StringTableIsAliveCounter : public BoolObjectClosure { BoolObjectClosure* _real_boc; public: @@ -380,12 +396,6 @@ } }; -void StringTable::trigger_concurrent_work() { - MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag); - the_table()->_has_work = true; - Service_lock->notify_all(); -} - void StringTable::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f, int* processed, int* removed) { DoNothingClosure dnc; @@ -434,30 +444,7 @@ _par_state_string->oops_do(f); } -struct StringTableDeleteCheck { - long _count; - long _item; - StringTableDeleteCheck() : _count(0), _item(0) {} - bool operator()(WeakHandle* val) { - ++_item; - oop tmp = val->peek(); - if (tmp == NULL) { - ++_count; - return true; - } else { - return false; - } - } -}; - -struct StringTableDoDelete { - long _count; - StringTableDoDelete() : _count(0) {} - void operator()(WeakHandle* val) { - ++_count; - } -}; - +// Concurrent work void StringTable::grow(JavaThread* jt) { StringTableHash::GrowTask gt(_local_table); if (!gt.prepare(jt)) { @@ -479,6 +466,30 @@ log_debug(stringtable)("Growed to size:" SIZE_FORMAT, _current_size); } +struct StringTableDoDelete : StackObj { + long _count; + StringTableDoDelete() : _count(0) {} + void operator()(WeakHandle* val) { + ++_count; + } +}; + +struct StringTableDeleteCheck : StackObj { + long _count; + long _item; + StringTableDeleteCheck() : _count(0), _item(0) {} + bool operator()(WeakHandle* val) { + ++_item; + oop tmp = val->peek(); + if (tmp == NULL) { + ++_count; + return true; + } else { + return false; + } + } +}; + void StringTable::clean_dead_entries(JavaThread* jt) { StringTableHash::BulkDeleteTask bdt(_local_table); if (!bdt.prepare(jt)) { @@ -513,26 +524,26 @@ if (_has_work) { return; } - double fact = StringTable::get_load_factor(); - double dead_fact = StringTable::get_dead_factor(); + double load_factor = StringTable::get_load_factor(); + double dead_factor = StringTable::get_dead_factor(); // We should clean/resize if we have more dead than alive, // more items than preferred load factor or // more dead items than water mark. - if ((dead_fact > fact) || - (fact > PREF_AVG_LIST_LEN) || - (dead_fact > CLEAN_DEAD_HIGH_WATER_MARK)) { + if ((dead_factor > load_factor) || + (load_factor > PREF_AVG_LIST_LEN) || + (dead_factor > CLEAN_DEAD_HIGH_WATER_MARK)) { log_debug(stringtable)("Concurrent work triggered, live factor:%g dead factor:%g", - fact, dead_fact); + load_factor, dead_factor); trigger_concurrent_work(); } } void StringTable::concurrent_work(JavaThread* jt) { _has_work = false; - double fact = get_load_factor(); - log_debug(stringtable, perf)("Concurrent work, live factor: %g", fact); + double load_factor = get_load_factor(); + log_debug(stringtable, perf)("Concurrent work, live factor: %g", load_factor); // We prefer growing, since that also removes dead items - if (fact > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) { + if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) { grow(jt); } else { clean_dead_entries(jt); @@ -543,6 +554,7 @@ StringTable::the_table()->concurrent_work(jt); } +// Rehash bool StringTable::do_rehash() { if (!_local_table->is_safepoint_safe()) { return false; @@ -600,12 +612,7 @@ StringTable::the_table()->try_rehash_table(); } -oop StringTable::lookup_shared(jchar* name, int len, unsigned int hash) { - assert(hash == java_lang_String::hash_code(name, len), - "hash must be computed using java_lang_String::hash_code"); - return _shared_table.lookup((const char*)name, hash, len); -} - +// Statistics static int literal_size(oop obj) { // NOTE: this would over-count if (pre-JDK8) // java_lang_Class::has_offset_field() is true and the String.value array is @@ -620,7 +627,7 @@ } } -struct SizeFunc { +struct SizeFunc : StackObj { size_t operator()(WeakHandle* val) { oop s = val->peek(); if (s == NULL) { @@ -637,7 +644,67 @@ _local_table->statistics_to(Thread::current(), sz, st, table_name); } -class PrintString { +// Verification +class VerifyStrings : StackObj { + public: + bool operator()(WeakHandle* val) { + oop s = val->peek(); + if (s != NULL) { + assert(java_lang_String::length(s) >= 0, "Length on string must work."); + } + return true; + }; +}; + +// This verification is part of Universe::verify() and needs to be quick. +void StringTable::verify() { + Thread* thr = Thread::current(); + VerifyStrings vs; + if (!the_table()->_local_table->try_scan(thr, vs)) { + log_info(stringtable)("verify unavailable at this moment"); + } +} + +// Verification and comp +class VerifyCompStrings : StackObj { + GrowableArray* _oops; + public: + size_t _errors; + VerifyCompStrings(GrowableArray* oops) : _oops(oops), _errors(0) {} + bool operator()(WeakHandle* val) { + oop s = val->resolve(); + if (s == NULL) { + return true; + } + int len = _oops->length(); + for (int i = 0; i < len; i++) { + bool eq = java_lang_String::equals(s, _oops->at(i)); + assert(!eq, "Duplicate strings"); + if (eq) { + _errors++; + } + } + _oops->push(s); + return true; + }; +}; + +size_t StringTable::verify_and_compare_entries() { + Thread* thr = Thread::current(); + GrowableArray* oops = + new (ResourceObj::C_HEAP, mtInternal) + GrowableArray((int)the_table()->_current_size, true); + + VerifyCompStrings vcs(oops); + if (!the_table()->_local_table->try_scan(thr, vcs)) { + log_info(stringtable)("verify unavailable at this moment"); + } + delete oops; + return vcs._errors; +} + +// Dumping +class PrintString : StackObj { Thread* _thr; outputStream* _st; public: @@ -688,26 +755,6 @@ } } -class VerifyStrings { - public: - bool operator()(WeakHandle* val) { - oop s = val->peek(); - if (s != NULL) { - assert(java_lang_String::length(s) >= 0, "Length on string must work."); - } - return true; - }; -}; - -// This verification is part of Universe::verify() and needs to be quick. -void StringTable::verify() { - Thread* thr = Thread::current(); - VerifyStrings vs; - if (!the_table()->_local_table->try_scan(thr, vs)) { - log_info(stringtable)("verify unavailable at this moment"); - } -} - // Utility for dumping strings StringtableDCmd::StringtableDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), @@ -733,8 +780,14 @@ } } -#if INCLUDE_CDS_JAVA_HEAP // Sharing +#if INCLUDE_CDS_JAVA_HEAP +oop StringTable::lookup_shared(jchar* name, int len, unsigned int hash) { + assert(hash == java_lang_String::hash_code(name, len), + "hash must be computed using java_lang_String::hash_code"); + return _shared_table.lookup((const char*)name, hash, len); +} + oop StringTable::create_archived_string(oop s, Thread* THREAD) { assert(DumpSharedSpaces, "this function is only used with -Xshare:dump"); @@ -755,9 +808,9 @@ return new_s; } -struct CopyArchive { +struct CopyToArchive : StackObj { CompactStringTableWriter* _writer; - CopyArchive(CompactStringTableWriter* writer) : _writer(writer) {} + CopyToArchive(CompactStringTableWriter* writer) : _writer(writer) {} bool operator()(WeakHandle* val) { oop s = val->peek(); if (s == NULL) { @@ -781,10 +834,10 @@ } }; -void StringTable::copy_shared_string(CompactStringTableWriter* writer) { +void StringTable::copy_shared_string_table(CompactStringTableWriter* writer) { assert(MetaspaceShared::is_heap_object_archiving_allowed(), "must be"); - CopyArchive copy(writer); + CopyToArchive copy(writer); StringTable::the_table()->_local_table->do_scan(Thread::current(), copy); } @@ -798,7 +851,7 @@ &MetaspaceShared::stats()->string); // Copy the interned strings into the "string space" within the java heap - copy_shared_string(&writer); + copy_shared_string_table(&writer); writer.dump(&_shared_table); } @@ -817,5 +870,4 @@ void StringTable::shared_oops_do(OopClosure* f) { _shared_table.oops_do(f); } - #endif //INCLUDE_CDS_JAVA_HEAP --- old/src/hotspot/share/classfile/stringTable.hpp 2018-06-04 17:06:50.806498481 +0200 +++ new/src/hotspot/share/classfile/stringTable.hpp 2018-06-04 17:06:50.376484056 +0200 @@ -71,9 +71,9 @@ OopStorage* _weak_handles; volatile size_t _items; - DEFINE_PAD_MINUS_SIZE(1, 64, sizeof(volatile size_t)); + DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile size_t)); volatile size_t _uncleaned_items; - DEFINE_PAD_MINUS_SIZE(2, 64, sizeof(volatile size_t)); + DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile size_t)); double get_load_factor(); double get_dead_factor(); @@ -146,19 +146,21 @@ { return StringTable::the_table()->_needs_rehashing; } // Sharing - oop lookup_shared(jchar* name, int len, unsigned int hash); + private: + oop lookup_shared(jchar* name, int len, unsigned int hash) NOT_CDS_JAVA_HEAP_RETURN_(NULL); + static void copy_shared_string_table(CompactStringTableWriter* ch_table) NOT_CDS_JAVA_HEAP_RETURN; + public: static oop create_archived_string(oop s, Thread* THREAD); static void set_shared_string_mapped() { _shared_string_mapped = true; } static bool shared_string_mapped() { return _shared_string_mapped; } static void shared_oops_do(OopClosure* f) NOT_CDS_JAVA_HEAP_RETURN; - static void copy_shared_string(CompactStringTableWriter* ch_table) - NOT_CDS_JAVA_HEAP_RETURN; static void write_to_archive() NOT_CDS_JAVA_HEAP_RETURN; static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; // Jcmd static void dump(outputStream* st, bool verbose=false); // Debugging + static size_t verify_and_compare_entries(); static void verify(); }; --- old/src/hotspot/share/gc/shared/genCollectedHeap.cpp 2018-06-04 17:06:51.878534444 +0200 +++ new/src/hotspot/share/gc/shared/genCollectedHeap.cpp 2018-06-04 17:06:51.463520522 +0200 @@ -859,9 +859,9 @@ // from the StringTable are the individual tasks. // Either we should be single threaded or have a ParState - assert((scope->n_threads() <= 1) || par_state_string != NULL, "Parallel but not ParState"); + assert((scope->n_threads() <= 1) || par_state_string != NULL, "Parallel but no ParState"); - if (scope->n_threads() > 1 && par_state_string != NULL) { + if (scope->n_threads() > 1) { StringTable::possibly_parallel_oops_do(par_state_string, root_closure); } else { StringTable::oops_do(root_closure); --- old/src/hotspot/share/runtime/globals.hpp 2018-06-04 17:06:52.986571614 +0200 +++ new/src/hotspot/share/runtime/globals.hpp 2018-06-04 17:06:52.571557692 +0200 @@ -2545,8 +2545,9 @@ "Relax the access control checks in the verifier") \ \ product(uintx, StringTableSize, defaultStringTableSize, \ - "Number of buckets in the interned String table") \ - range(minimumStringTableSize, 111*defaultStringTableSize) \ + "Number of buckets in the interned String table " \ + "(will be rounded to nearest higher power of 2)") \ + range(minimumStringTableSize, 16777216ul) \ \ experimental(uintx, SymbolTableSize, defaultSymbolTableSize, \ "Number of buckets in the JVM internal Symbol table") \ --- old/src/hotspot/share/runtime/java.cpp 2018-06-04 17:06:54.137610227 +0200 +++ new/src/hotspot/share/runtime/java.cpp 2018-06-04 17:06:53.725596406 +0200 @@ -523,6 +523,14 @@ BeforeExit_lock->notify_all(); } + if (VerifyStringTableAtExit) { + size_t fail_cnt = StringTable::verify_and_compare_entries(); + if (fail_cnt != 0) { + tty->print_cr("ERROR: fail_cnt=" SIZE_FORMAT, fail_cnt); + guarantee(fail_cnt == 0, "unexpected StringTable verification failures"); + } + } + #undef BEFORE_EXIT_NOT_RUN #undef BEFORE_EXIT_RUNNING #undef BEFORE_EXIT_DONE --- old/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp 2018-06-04 17:06:55.229646861 +0200 +++ new/src/hotspot/share/utilities/concurrentHashTableTasks.inline.hpp 2018-06-04 17:06:54.816633006 +0200 @@ -25,6 +25,7 @@ #ifndef SHARE_UTILITIES_CONCURRENT_HASH_TABLE_TASKS_INLINE_HPP #define SHARE_UTILITIES_CONCURRENT_HASH_TABLE_TASKS_INLINE_HPP +#include "utilities/globalDefinitions.hpp" #include "utilities/concurrentHashTable.inline.hpp" // This inline file contains BulkDeleteTask and GrowTasks which are both bucket @@ -63,6 +64,7 @@ // Calculate starting values. void setup() { _size_log2 = _cht->_table->_log2_size; + _task_size_log2 = MIN2(_task_size_log2, _size_log2); size_t tmp = _size_log2 > _task_size_log2 ? _size_log2 - _task_size_log2 : 0; _stop_task = (((size_t)1) << tmp); --- old/src/hotspot/share/utilities/globalDefinitions.hpp 2018-06-04 17:06:56.320683461 +0200 +++ new/src/hotspot/share/utilities/globalDefinitions.hpp 2018-06-04 17:06:55.903669472 +0200 @@ -424,8 +424,8 @@ //---------------------------------------------------------------------------------------------------- // Default and minimum StringTableSize values -const int defaultStringTableSize = NOT_LP64(1009) LP64_ONLY(60013); -const int minimumStringTableSize = 1009; +const int defaultStringTableSize = NOT_LP64(1024) LP64_ONLY(65536); +const int minimumStringTableSize = 128; const int defaultSymbolTableSize = 20011; const int minimumSymbolTableSize = 1009;