1 /*
2 * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "cds/aotMappedHeapLoader.hpp"
26 #include "cds/archiveBuilder.hpp"
27 #include "cds/cdsConfig.hpp"
28 #include "cds/heapShared.inline.hpp"
29 #include "classfile/altHashing.hpp"
30 #include "classfile/compactHashtable.hpp"
31 #include "classfile/javaClasses.inline.hpp"
32 #include "classfile/stringTable.hpp"
33 #include "classfile/vmClasses.hpp"
34 #include "compiler/compileBroker.hpp"
35 #include "gc/shared/collectedHeap.hpp"
36 #include "gc/shared/oopStorage.inline.hpp"
37 #include "gc/shared/oopStorageSet.hpp"
38 #include "gc/shared/stringdedup/stringDedup.hpp"
39 #include "logging/log.hpp"
40 #include "logging/logStream.hpp"
41 #include "memory/allocation.inline.hpp"
42 #include "memory/oopFactory.hpp"
43 #include "memory/resourceArea.hpp"
44 #include "oops/access.inline.hpp"
45 #include "oops/compressedOops.hpp"
46 #include "oops/oop.inline.hpp"
47 #include "oops/typeArrayOop.inline.hpp"
48 #include "oops/weakHandle.inline.hpp"
49 #include "runtime/atomicAccess.hpp"
50 #include "runtime/handles.inline.hpp"
51 #include "runtime/interfaceSupport.inline.hpp"
52 #include "runtime/mutexLocker.hpp"
53 #include "runtime/safepointVerifiers.hpp"
54 #include "runtime/timerTrace.hpp"
55 #include "runtime/trimNativeHeap.hpp"
56 #include "runtime/vmOperations.hpp"
57 #include "services/diagnosticCommand.hpp"
58 #include "utilities/concurrentHashTable.inline.hpp"
59 #include "utilities/concurrentHashTableTasks.inline.hpp"
60 #include "utilities/macros.hpp"
61 #include "utilities/resizableHashTable.hpp"
62 #include "utilities/utf8.hpp"
63 #if INCLUDE_G1GC
64 #include "gc/g1/g1CollectedHeap.hpp"
65 #endif
66
67 // We prefer short chains of avg 2
68 const double PREF_AVG_LIST_LEN = 2.0;
69 // 2^24 is max size
70 const size_t END_SIZE = 24;
71 // If a chain gets to 100 something might be wrong
72 const size_t REHASH_LEN = 100;
73 // If we have as many dead items as 50% of the number of bucket
74 const double CLEAN_DEAD_HIGH_WATER_MARK = 0.5;
75
76 #if INCLUDE_CDS_JAVA_HEAP
77 inline oop StringTable::read_string_from_compact_hashtable(address base_address, u4 index) {
78 assert(AOTMappedHeapLoader::is_in_use(), "sanity");
79 oop s = HeapShared::get_root((int)index, false);
80 assert(java_lang_String::is_instance(s), "must be");
81 return s;
82 }
83
84 typedef CompactHashtable<
85 const StringTable::StringWrapper&, oop,
86 StringTable::read_string_from_compact_hashtable,
87 StringTable::wrapped_string_equals> SharedStringTable;
88
89 static SharedStringTable _shared_table;
90 #endif
91
92 // --------------------------------------------------------------------------
93
94 typedef ConcurrentHashTable<StringTableConfig, mtSymbol> StringTableHash;
95 static StringTableHash* _local_table = nullptr;
96
97 volatile bool StringTable::_has_work = false;
98 volatile bool StringTable::_needs_rehashing = false;
99 OopStorage* StringTable::_oop_storage;
100
101 static size_t _current_size = 0;
102 static volatile size_t _items_count = 0;
103
104 volatile bool _alt_hash = false;
105
106 static bool _rehashed = false;
107 static uint64_t _alt_hash_seed = 0;
108
109 enum class StringType {
110 OopStr, UnicodeStr, SymbolStr, UTF8Str
111 };
112
113 struct StringWrapperInternal {
114 union {
115 const Handle oop_str;
116 const jchar* unicode_str;
117 const Symbol* symbol_str;
118 const char* utf8_str;
119 };
120 const StringType type;
121 const size_t length;
122
123 StringWrapperInternal(const Handle oop_str, const size_t length) : oop_str(oop_str), type(StringType::OopStr), length(length) {}
124 StringWrapperInternal(const jchar* unicode_str, const size_t length) : unicode_str(unicode_str), type(StringType::UnicodeStr), length(length) {}
125 StringWrapperInternal(const Symbol* symbol_str, const size_t length) : symbol_str(symbol_str), type(StringType::SymbolStr), length(length) {}
126 StringWrapperInternal(const char* utf8_str, const size_t length) : utf8_str(utf8_str), type(StringType::UTF8Str), length(length) {}
127 };
128
129 static unsigned int hash_string(const jchar* s, int len, bool useAlt) {
130 return useAlt ?
131 AltHashing::halfsiphash_32(_alt_hash_seed, s, len) :
132 java_lang_String::hash_code(s, len);
133 }
134
135 const char* StringTable::get_symbol_utf8(const StringWrapper& symbol) {
136 return reinterpret_cast<const char*>(symbol.symbol_str->bytes());
137 }
138
139 unsigned int StringTable::hash_wrapped_string(const StringWrapper& wrapped_str) {
140 switch (wrapped_str.type) {
141 case StringType::OopStr:
142 return java_lang_String::hash_code(wrapped_str.oop_str());
143 case StringType::UnicodeStr:
144 return java_lang_String::hash_code(wrapped_str.unicode_str, static_cast<int>(wrapped_str.length));
145 case StringType::SymbolStr:
146 return java_lang_String::hash_code(get_symbol_utf8(wrapped_str), wrapped_str.length);
147 case StringType::UTF8Str:
148 return java_lang_String::hash_code(wrapped_str.utf8_str, wrapped_str.length);
149 default:
150 ShouldNotReachHere();
151 }
152 return 0;
153 }
154
155 // Unnamed int needed to fit CompactHashtable's equals type signature
156 bool StringTable::wrapped_string_equals(oop java_string, const StringWrapper& wrapped_str, int) {
157 switch (wrapped_str.type) {
158 case StringType::OopStr:
159 return java_lang_String::equals(java_string, wrapped_str.oop_str());
160 case StringType::UnicodeStr:
161 return java_lang_String::equals(java_string, wrapped_str.unicode_str, static_cast<int>(wrapped_str.length));
162 case StringType::SymbolStr:
163 return java_lang_String::equals(java_string, get_symbol_utf8(wrapped_str), wrapped_str.length);
164 case StringType::UTF8Str:
165 return java_lang_String::equals(java_string, wrapped_str.utf8_str, wrapped_str.length);
166 default:
167 ShouldNotReachHere();
168 }
169 return false;
170 }
171
172 class StringTableConfig : public StackObj {
173 private:
174 public:
175 typedef WeakHandle Value;
176
177 static uintx get_hash(Value const& value, bool* is_dead) {
178 oop val_oop = value.peek();
179 if (val_oop == nullptr) {
180 *is_dead = true;
181 return 0;
182 }
183 *is_dead = false;
184 ResourceMark rm;
185 // All String oops are hashed as unicode
186 int length;
187 jchar* chars = java_lang_String::as_unicode_string_or_null(val_oop, length);
188 if (chars != nullptr) {
189 return hash_string(chars, length, _alt_hash);
190 }
191 vm_exit_out_of_memory(length, OOM_MALLOC_ERROR, "get hash from oop");
192 return 0;
193 }
194 // We use default allocation/deallocation but counted
195 static void* allocate_node(void* context, size_t size, Value const& value) {
196 StringTable::item_added();
197 return AllocateHeap(size, mtSymbol);
198 }
199 static void free_node(void* context, void* memory, Value& value) {
200 value.release(StringTable::_oop_storage);
201 FreeHeap(memory);
202 StringTable::item_removed();
203 }
204 };
205
206 class StringTableLookup : StackObj {
207 uintx _hash;
208
209 protected:
210 Thread* _thread;
211 Handle _found;
212
213 public:
214 StringTableLookup(Thread* thread, uintx hash)
215 : _hash(hash), _thread(thread) {}
216 uintx get_hash() const { return _hash; }
217 bool is_dead(WeakHandle* value) {
218 oop val_oop = value->peek();
219 return val_oop == nullptr;
220 }
221 };
222
223 class StringTableLookupUnicode : public StringTableLookup {
224 private:
225 const jchar* _str;
226 int _len;
227
228 public:
229 StringTableLookupUnicode(Thread* thread, uintx hash, const jchar* key, int len)
230 : StringTableLookup(thread, hash), _str(key), _len(len) {}
231
232 bool equals(const WeakHandle* value) {
233 oop val_oop = value->peek();
234 if (val_oop == nullptr) {
235 return false;
236 }
237 bool equals = java_lang_String::equals(val_oop, _str, _len);
238 if (!equals) {
239 return false;
240 }
241 // Need to resolve weak handle and Handleize through possible safepoint.
242 _found = Handle(_thread, value->resolve());
243 return true;
244 }
245 };
246
247 class StringTableLookupUTF8 : public StringTableLookup {
248 private:
249 const char* _str;
250 size_t _utf8_len;
251
252 public:
253 StringTableLookupUTF8(Thread* thread, uintx hash, const char* key, size_t utf8_len)
254 : StringTableLookup(thread, hash), _str(key), _utf8_len(utf8_len) {}
255
256 bool equals(const WeakHandle* value) {
257 oop val_oop = value->peek();
258 if (val_oop == nullptr) {
259 return false;
260 }
261 bool equals = java_lang_String::equals(val_oop, _str, _utf8_len);
262 if (!equals) {
263 return false;
264 }
265 // Need to resolve weak handle and Handleize through possible safepoint.
266 _found = Handle(_thread, value->resolve());
267 return true;
268 }
269 };
270
271 class StringTableLookupOop : public StringTableLookup {
272 private:
273 Handle _find;
274
275 public:
276 StringTableLookupOop(Thread* thread, uintx hash, Handle handle)
277 : StringTableLookup(thread, hash), _find(handle) {}
278
279 bool equals(WeakHandle* value) {
280 oop val_oop = value->peek();
281 if (val_oop == nullptr) {
282 return false;
283 }
284 bool equals = java_lang_String::equals(_find(), val_oop);
285 if (!equals) {
286 return false;
287 }
288 // Need to resolve weak handle and Handleize through possible safepoint.
289 _found = Handle(_thread, value->resolve());
290 return true;
291 }
292 };
293
294 void StringTable::create_table() {
295 size_t start_size_log_2 = log2i_ceil(StringTableSize);
296 _current_size = ((size_t)1) << start_size_log_2;
297 log_trace(stringtable)("Start size: %zu (%zu)",
298 _current_size, start_size_log_2);
299 _local_table = new StringTableHash(start_size_log_2, END_SIZE, REHASH_LEN, true);
300 _oop_storage = OopStorageSet::create_weak("StringTable Weak", mtSymbol);
301 _oop_storage->register_num_dead_callback(&gc_notification);
302 }
303
304 void StringTable::item_added() {
305 AtomicAccess::inc(&_items_count);
306 }
307
308 void StringTable::item_removed() {
309 AtomicAccess::dec(&_items_count);
310 }
311
312 double StringTable::get_load_factor() {
313 return double(_items_count)/double(_current_size);
314 }
315
316 double StringTable::get_dead_factor(size_t num_dead) {
317 return double(num_dead)/double(_current_size);
318 }
319
320 size_t StringTable::table_size() {
321 return ((size_t)1) << _local_table->get_size_log2(Thread::current());
322 }
323
324 bool StringTable::has_work() {
325 return AtomicAccess::load_acquire(&_has_work);
326 }
327
328 size_t StringTable::items_count_acquire() {
329 return AtomicAccess::load_acquire(&_items_count);
330 }
331
332 void StringTable::trigger_concurrent_work() {
333 // Avoid churn on ServiceThread
334 if (!has_work()) {
335 MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
336 AtomicAccess::store(&_has_work, true);
337 Service_lock->notify_all();
338 }
339 }
340
341 // Probing
342 oop StringTable::lookup(Symbol* symbol) {
343 ResourceMark rm;
344 int length;
345 jchar* chars = symbol->as_unicode(length);
346 return lookup(chars, length);
347 }
348
349 oop StringTable::lookup(const jchar* name, int len) {
350 unsigned int hash = java_lang_String::hash_code(name, len);
351 StringWrapper wrapped_name(name, len);
352 oop string = lookup_shared(wrapped_name, hash);
353 if (string != nullptr) {
354 return string;
355 }
356 if (_alt_hash) {
357 hash = hash_string(name, len, true);
358 }
359 return do_lookup(wrapped_name, hash);
360 }
361
362 class StringTableGet : public StackObj {
363 Thread* _thread;
364 Handle _return;
365 public:
366 StringTableGet(Thread* thread) : _thread(thread) {}
367 void operator()(WeakHandle* val) {
368 oop result = val->resolve();
369 assert(result != nullptr, "Result should be reachable");
370 _return = Handle(_thread, result);
371 }
372 oop get_res_oop() {
373 return _return();
374 }
375 };
376
377 void StringTable::update_needs_rehash(bool rehash) {
378 if (rehash) {
379 _needs_rehashing = true;
380 trigger_concurrent_work();
381 }
382 }
383
384 oop StringTable::do_lookup(const StringWrapper& name, uintx hash) {
385 Thread* thread = Thread::current();
386 StringTableGet stg(thread);
387 bool rehash_warning;
388
389 switch (name.type) {
390 case StringType::OopStr: {
391 StringTableLookupOop lookup(thread, hash, name.oop_str);
392 _local_table->get(thread, lookup, stg, &rehash_warning);
393 break;
394 }
395 case StringType::UnicodeStr: {
396 StringTableLookupUnicode lookup(thread, hash, name.unicode_str, static_cast<int>(name.length));
397 _local_table->get(thread, lookup, stg, &rehash_warning);
398 break;
399 }
400 case StringType::SymbolStr: {
401 StringTableLookupUTF8 lookup(thread, hash, get_symbol_utf8(name), name.length);
402 _local_table->get(thread, lookup, stg, &rehash_warning);
403 break;
404 }
405 case StringType::UTF8Str: {
406 StringTableLookupUTF8 lookup(thread, hash, name.utf8_str, name.length);
407 _local_table->get(thread, lookup, stg, &rehash_warning);
408 break;
409 }
410 default:
411 ShouldNotReachHere();
412 }
413
414 update_needs_rehash(rehash_warning);
415 return stg.get_res_oop();
416 }
417
418 // Converts and allocates to a unicode string and stores the unicode length in len
419 const jchar* StringTable::to_unicode(const StringWrapper& wrapped_str, int &len, TRAPS) {
420 switch (wrapped_str.type) {
421 case StringType::UnicodeStr:
422 len = static_cast<int>(wrapped_str.length);
423 return wrapped_str.unicode_str;
424 case StringType::OopStr:
425 return java_lang_String::as_unicode_string(wrapped_str.oop_str(), len, CHECK_NULL);
426 case StringType::SymbolStr: {
427 const char* utf8_str = get_symbol_utf8(wrapped_str);
428 int unicode_length = UTF8::unicode_length(utf8_str, wrapped_str.symbol_str->utf8_length());
429 jchar* chars = NEW_RESOURCE_ARRAY(jchar, unicode_length);
430 UTF8::convert_to_unicode(utf8_str, chars, unicode_length);
431 len = unicode_length;
432 return chars;
433 }
434 case StringType::UTF8Str: {
435 int unicode_length = UTF8::unicode_length(wrapped_str.utf8_str);
436 jchar* chars = NEW_RESOURCE_ARRAY(jchar, unicode_length);
437 UTF8::convert_to_unicode(wrapped_str.utf8_str, chars, unicode_length);
438 len = unicode_length;
439 return chars;
440 }
441 default:
442 ShouldNotReachHere();
443 }
444 return nullptr;
445 }
446
447 Handle StringTable::handle_from_wrapped_string(const StringWrapper& wrapped_str, TRAPS) {
448 switch (wrapped_str.type) {
449 case StringType::OopStr:
450 return wrapped_str.oop_str;
451 case StringType::UnicodeStr:
452 return java_lang_String::create_from_unicode(wrapped_str.unicode_str, static_cast<int>(wrapped_str.length), THREAD);
453 case StringType::SymbolStr:
454 return java_lang_String::create_from_symbol(wrapped_str.symbol_str, THREAD);
455 case StringType::UTF8Str:
456 return java_lang_String::create_from_str(wrapped_str.utf8_str, THREAD);
457 default:
458 ShouldNotReachHere();
459 }
460 return Handle();
461 }
462
463 // Interning
464 oop StringTable::intern(Symbol* symbol, TRAPS) {
465 if (symbol == nullptr) return nullptr;
466 int length = symbol->utf8_length();
467 StringWrapper name(symbol, length);
468 oop result = intern(name, CHECK_NULL);
469 return result;
470 }
471
472 oop StringTable::intern(oop string, TRAPS) {
473 if (string == nullptr) return nullptr;
474 int length = java_lang_String::length(string);
475 Handle h_string (THREAD, string);
476 StringWrapper name(h_string, length);
477 oop result = intern(name, CHECK_NULL);
478 return result;
479 }
480
481 oop StringTable::intern(const char* utf8_string, TRAPS) {
482 if (utf8_string == nullptr) return nullptr;
483 size_t length = strlen(utf8_string);
484 StringWrapper name(utf8_string, length);
485 oop result = intern(name, CHECK_NULL);
486 return result;
487 }
488
489 oop StringTable::intern(const StringWrapper& name, TRAPS) {
490 // shared table always uses java_lang_String::hash_code
491 unsigned int hash = hash_wrapped_string(name);
492 oop found_string = lookup_shared(name, hash);
493 if (found_string != nullptr) {
494 return found_string;
495 }
496
497 if (_alt_hash) {
498 ResourceMark rm(THREAD);
499 // Convert to unicode for alt hashing
500 int unicode_length;
501 const jchar* chars = to_unicode(name, unicode_length, CHECK_NULL);
502 hash = hash_string(chars, unicode_length, true);
503 }
504
505 found_string = do_lookup(name, hash);
506 if (found_string != nullptr) {
507 return found_string;
508 }
509 return do_intern(name, hash, THREAD);
510 }
511
512 oop StringTable::do_intern(const StringWrapper& name, uintx hash, TRAPS) {
513 HandleMark hm(THREAD); // cleanup strings created
514 Handle string_h = handle_from_wrapped_string(name, CHECK_NULL);
515
516 assert(StringTable::wrapped_string_equals(string_h(), name),
517 "string must be properly initialized");
518
519 // Notify deduplication support that the string is being interned. A string
520 // must never be deduplicated after it has been interned. Doing so interferes
521 // with compiler optimizations done on e.g. interned string literals.
522 if (StringDedup::is_enabled()) {
523 StringDedup::notify_intern(string_h());
524 }
525
526 StringTableLookupOop lookup(THREAD, hash, string_h);
527 StringTableGet stg(THREAD);
528
529 bool rehash_warning;
530 do {
531 // Callers have already looked up the String, so just go to add.
532 WeakHandle wh(_oop_storage, string_h);
533 // The hash table takes ownership of the WeakHandle, even if it's not inserted.
534 if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) {
535 update_needs_rehash(rehash_warning);
536 return wh.resolve();
537 }
538 // In case another thread did a concurrent add, return value already in the table.
539 // This could fail if the String got gc'ed concurrently, so loop back until success.
540 if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) {
541 update_needs_rehash(rehash_warning);
542 return stg.get_res_oop();
543 }
544 } while(true);
545 }
546
547 // Concurrent work
548 void StringTable::grow(JavaThread* jt) {
549 StringTableHash::GrowTask gt(_local_table);
550 if (!gt.prepare(jt)) {
551 return;
552 }
553 log_trace(stringtable)("Started to grow");
554 {
555 TraceTime timer("Grow", TRACETIME_LOG(Debug, stringtable, perf));
556 while (gt.do_task(jt)) {
557 gt.pause(jt);
558 {
559 ThreadBlockInVM tbivm(jt);
560 }
561 gt.cont(jt);
562 }
563 }
564 gt.done(jt);
565 _current_size = table_size();
566 log_debug(stringtable)("Grown to size:%zu", _current_size);
567 }
568
569 struct StringTableDoDelete : StackObj {
570 void operator()(WeakHandle* val) {
571 /* do nothing */
572 }
573 };
574
575 struct StringTableDeleteCheck : StackObj {
576 long _count;
577 long _item;
578 StringTableDeleteCheck() : _count(0), _item(0) {}
579 bool operator()(WeakHandle* val) {
580 ++_item;
581 oop tmp = val->peek();
582 if (tmp == nullptr) {
583 ++_count;
584 return true;
585 } else {
586 return false;
587 }
588 }
589 };
590
591 void StringTable::clean_dead_entries(JavaThread* jt) {
592 // BulkDeleteTask::prepare() may take ConcurrentHashTableResize_lock (nosafepoint-2).
593 // When NativeHeapTrimmer is enabled, SuspendMark may take NativeHeapTrimmer::_lock (nosafepoint).
594 // Take SuspendMark first to keep lock order and avoid deadlock.
595 NativeHeapTrimmer::SuspendMark sm("stringtable");
596 StringTableHash::BulkDeleteTask bdt(_local_table);
597 if (!bdt.prepare(jt)) {
598 return;
599 }
600
601 StringTableDeleteCheck stdc;
602 StringTableDoDelete stdd;
603 {
604 TraceTime timer("Clean", TRACETIME_LOG(Debug, stringtable, perf));
605 while(bdt.do_task(jt, stdc, stdd)) {
606 bdt.pause(jt);
607 {
608 ThreadBlockInVM tbivm(jt);
609 }
610 bdt.cont(jt);
611 }
612 bdt.done(jt);
613 }
614 log_debug(stringtable)("Cleaned %ld of %ld", stdc._count, stdc._item);
615 }
616
617 void StringTable::gc_notification(size_t num_dead) {
618 log_trace(stringtable)("Uncleaned items:%zu", num_dead);
619
620 if (has_work()) {
621 return;
622 }
623
624 double load_factor = StringTable::get_load_factor();
625 double dead_factor = StringTable::get_dead_factor(num_dead);
626 // We should clean/resize if we have more dead than alive,
627 // more items than preferred load factor or
628 // more dead items than water mark.
629 if ((dead_factor > load_factor) ||
630 (load_factor > PREF_AVG_LIST_LEN) ||
631 (dead_factor > CLEAN_DEAD_HIGH_WATER_MARK)) {
632 log_debug(stringtable)("Concurrent work triggered, live factor: %g dead factor: %g",
633 load_factor, dead_factor);
634 trigger_concurrent_work();
635 }
636 }
637
638 bool StringTable::should_grow() {
639 return get_load_factor() > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached();
640 }
641
642 void StringTable::do_concurrent_work(JavaThread* jt) {
643 // Rehash if needed. Rehashing goes to a safepoint but the rest of this
644 // work is concurrent.
645 if (needs_rehashing() && maybe_rehash_table()) {
646 AtomicAccess::release_store(&_has_work, false);
647 return; // done, else grow
648 }
649 log_debug(stringtable, perf)("Concurrent work, live factor: %g", get_load_factor());
650 // We prefer growing, since that also removes dead items
651 if (should_grow()) {
652 grow(jt);
653 } else {
654 clean_dead_entries(jt);
655 }
656 AtomicAccess::release_store(&_has_work, false);
657 }
658
659 // Called at VM_Operation safepoint
660 void StringTable::rehash_table() {
661 assert(SafepointSynchronize::is_at_safepoint(), "must be called at safepoint");
662 // The ServiceThread initiates the rehashing so it is not resizing.
663 assert (_local_table->is_safepoint_safe(), "Should not be resizing now");
664
665 _alt_hash_seed = AltHashing::compute_seed();
666
667 // We use current size, not max size.
668 size_t new_size = _local_table->get_size_log2(Thread::current());
669 StringTableHash* new_table = new StringTableHash(new_size, END_SIZE, REHASH_LEN, true);
670 // Use alt hash from now on
671 _alt_hash = true;
672 _local_table->rehash_nodes_to(Thread::current(), new_table);
673
674 // free old table
675 delete _local_table;
676 _local_table = new_table;
677
678 _rehashed = true;
679 _needs_rehashing = false;
680 }
681
682 bool StringTable::maybe_rehash_table() {
683 log_debug(stringtable)("Table imbalanced, rehashing called.");
684
685 // Grow instead of rehash.
686 if (should_grow()) {
687 log_debug(stringtable)("Choosing growing over rehashing.");
688 _needs_rehashing = false;
689 return false;
690 }
691 // Already rehashed.
692 if (_rehashed) {
693 log_warning(stringtable)("Rehashing already done, still long lists.");
694 _needs_rehashing = false;
695 return false;
696 }
697
698 VM_RehashStringTable op;
699 VMThread::execute(&op);
700 return true; // return true because we tried.
701 }
702
703 // Statistics
704 static size_t literal_size(oop obj) {
705 if (obj == nullptr) {
706 return 0;
707 }
708
709 size_t word_size = obj->size();
710
711 if (obj->klass() == vmClasses::String_klass()) {
712 // This may overcount if String.value arrays are shared.
713 word_size += java_lang_String::value(obj)->size();
714 }
715
716 return word_size * HeapWordSize;
717 }
718
719 struct SizeFunc : StackObj {
720 size_t operator()(WeakHandle* val) {
721 oop s = val->peek();
722 if (s == nullptr) {
723 // Dead
724 return 0;
725 }
726 return literal_size(s);
727 };
728 };
729
730 TableStatistics StringTable::get_table_statistics() {
731 static TableStatistics ts;
732 SizeFunc sz;
733
734 Thread* jt = Thread::current();
735 StringTableHash::StatisticsTask sts(_local_table);
736 if (!sts.prepare(jt)) {
737 return ts; // return old table statistics
738 }
739 {
740 TraceTime timer("GetStatistics", TRACETIME_LOG(Debug, stringtable, perf));
741 while (sts.do_task(jt, sz)) {
742 sts.pause(jt);
743 if (jt->is_Java_thread()) {
744 ThreadBlockInVM tbivm(JavaThread::cast(jt));
745 }
746 sts.cont(jt);
747 }
748 }
749 ts = sts.done(jt);
750 return ts;
751 }
752
753 void StringTable::print_table_statistics(outputStream* st) {
754 TableStatistics ts = get_table_statistics();
755 ts.print(st, "StringTable");
756 #if INCLUDE_CDS_JAVA_HEAP
757 if (!_shared_table.empty()) {
758 _shared_table.print_table_statistics(st, "Shared String Table");
759 }
760 #endif
761 }
762
763 // Verification
764 class VerifyStrings : StackObj {
765 public:
766 bool operator()(WeakHandle* val) {
767 oop s = val->peek();
768 if (s != nullptr) {
769 assert(java_lang_String::length(s) >= 0, "Length on string must work.");
770 }
771 return true;
772 };
773 };
774
775 // This verification is part of Universe::verify() and needs to be quick.
776 void StringTable::verify() {
777 VerifyStrings vs;
778 _local_table->do_safepoint_scan(vs);
779 }
780
781 // Verification and comp
782 class StringTable::VerifyCompStrings : StackObj {
783 static unsigned string_hash(oop const& str) {
784 return java_lang_String::hash_code_noupdate(str);
785 }
786 static bool string_equals(oop const& a, oop const& b) {
787 return java_lang_String::equals(a, b);
788 }
789
790 ResizeableHashTable<oop, bool, AnyObj::C_HEAP, mtInternal,
791 string_hash, string_equals> _table;
792 public:
793 size_t _errors;
794 VerifyCompStrings() : _table(unsigned(items_count_acquire() / 8) + 1, 0 /* do not resize */), _errors(0) {}
795 bool operator()(WeakHandle* val) {
796 oop s = val->resolve();
797 if (s == nullptr) {
798 return true;
799 }
800 bool created;
801 _table.put_if_absent(s, true, &created);
802 assert(created, "Duplicate strings");
803 if (!created) {
804 _errors++;
805 }
806 return true;
807 };
808 };
809
810 size_t StringTable::verify_and_compare_entries() {
811 Thread* thr = Thread::current();
812 VerifyCompStrings vcs;
813 _local_table->do_scan(thr, vcs);
814 return vcs._errors;
815 }
816
817 static void print_string(Thread* current, outputStream* st, oop s) {
818 typeArrayOop value = java_lang_String::value_no_keepalive(s);
819 int length = java_lang_String::length(s);
820 bool is_latin1 = java_lang_String::is_latin1(s);
821
822 if (length <= 0) {
823 st->print("%d: ", length);
824 } else {
825 ResourceMark rm(current);
826 size_t utf8_length = length;
827 char* utf8_string;
828
829 if (!is_latin1) {
830 jchar* chars = value->char_at_addr(0);
831 utf8_string = UNICODE::as_utf8(chars, utf8_length);
832 } else {
833 jbyte* bytes = value->byte_at_addr(0);
834 utf8_string = UNICODE::as_utf8(bytes, utf8_length);
835 }
836
837 st->print("%zu: ", utf8_length);
838 HashtableTextDump::put_utf8(st, utf8_string, utf8_length);
839 }
840 st->cr();
841 }
842
843 // Dumping
844 class PrintString : StackObj {
845 Thread* _thr;
846 outputStream* _st;
847 public:
848 PrintString(Thread* thr, outputStream* st) : _thr(thr), _st(st) {}
849 bool operator()(WeakHandle* val) {
850 oop s = val->peek();
851 if (s == nullptr) {
852 return true;
853 }
854 print_string(_thr, _st, s);
855 return true;
856 };
857 };
858
859 class PrintSharedString : StackObj {
860 Thread* _thr;
861 outputStream* _st;
862 public:
863 PrintSharedString(Thread* thr, outputStream* st) : _thr(thr), _st(st) {}
864 void do_value(oop s) {
865 if (s == nullptr) {
866 return;
867 }
868 print_string(_thr, _st, s);
869 };
870 };
871
872 void StringTable::dump(outputStream* st, bool verbose) {
873 if (!verbose) {
874 print_table_statistics(st);
875 } else {
876 Thread* thr = Thread::current();
877 ResourceMark rm(thr);
878 st->print_cr("VERSION: 1.1");
879 PrintString ps(thr, st);
880 if (!_local_table->try_scan(thr, ps)) {
881 st->print_cr("dump unavailable at this moment");
882 }
883 #if INCLUDE_CDS_JAVA_HEAP
884 if (!_shared_table.empty()) {
885 st->print_cr("#----------------");
886 st->print_cr("# Shared strings:");
887 st->print_cr("#----------------");
888 PrintSharedString pss(thr, st);
889 _shared_table.iterate_all(&pss);
890 }
891 #endif
892 }
893 }
894
895 // Utility for dumping strings
896 StringtableDCmd::StringtableDCmd(outputStream* output, bool heap) :
897 DCmdWithParser(output, heap),
898 _verbose("-verbose", "Dump the content of each string in the table",
899 "BOOLEAN", false, "false") {
900 _dcmdparser.add_dcmd_option(&_verbose);
901 }
902
903 void StringtableDCmd::execute(DCmdSource source, TRAPS) {
904 VM_DumpHashtable dumper(output(), VM_DumpHashtable::DumpStrings,
905 _verbose.value());
906 VMThread::execute(&dumper);
907 }
908
909 // Sharing
910 #if INCLUDE_CDS_JAVA_HEAP
911 size_t StringTable::shared_entry_count() {
912 assert(HeapShared::is_loading_mapping_mode(), "should not reach here");
913 return _shared_table.entry_count();
914 }
915
916 oop StringTable::lookup_shared(const StringWrapper& name, unsigned int hash) {
917 if (!AOTMappedHeapLoader::is_in_use()) {
918 return nullptr;
919 }
920 assert(hash == hash_wrapped_string(name),
921 "hash must be computed using java_lang_String::hash_code");
922 // len is required but is already part of StringWrapper, so 0 is used
923 return _shared_table.lookup(name, hash, 0);
924 }
925
926 oop StringTable::lookup_shared(const jchar* name, int len) {
927 if (!AOTMappedHeapLoader::is_in_use()) {
928 return nullptr;
929 }
930 StringWrapper wrapped_name(name, len);
931 // len is required but is already part of StringWrapper, so 0 is used
932 return _shared_table.lookup(wrapped_name, java_lang_String::hash_code(name, len), 0);
933 }
934
935 void StringTable::init_shared_table() {
936 assert(SafepointSynchronize::is_at_safepoint(), "inside AOT safepoint");
937 precond(CDSConfig::is_dumping_heap());
938 assert(HeapShared::is_writing_mapping_mode(), "not used for streamed oops");
939
940 int n = 0;
941 auto copy_into_aot_heap = [&] (WeakHandle* val) {
942 oop string = val->peek();
943 if (string != nullptr && !HeapShared::is_string_too_large_to_archive(string)) {
944 // If string is too large, don't put it into the string table.
945 // - If there are no other references to it, it won't be stored into the archive,
946 // so we are all good.
947 // - If there's a reference to it, we will report an error inside HeapShared.cpp and
948 // dumping will fail.
949 HeapShared::add_to_dumped_interned_strings(string);
950 }
951 n++;
952 return true;
953 };
954
955 _local_table->do_safepoint_scan(copy_into_aot_heap);
956 log_info(aot)("Archived %d interned strings", n);
957 };
958
959 void StringTable::write_shared_table() {
960 assert(SafepointSynchronize::is_at_safepoint(), "inside AOT safepoint");
961 precond(CDSConfig::is_dumping_heap());
962 assert(HeapShared::is_writing_mapping_mode(), "not used for streamed oops");
963
964 _shared_table.reset();
965 CompactHashtableWriter writer((int)items_count_acquire(), ArchiveBuilder::string_stats());
966
967 auto copy_into_shared_table = [&] (WeakHandle* val) {
968 oop string = val->peek();
969 if (string != nullptr && !HeapShared::is_string_too_large_to_archive(string)) {
970 unsigned int hash = java_lang_String::hash_code(string);
971 int root_id = HeapShared::append_root(string);
972 writer.add(hash, root_id);
973 }
974 return true;
975 };
976 _local_table->do_safepoint_scan(copy_into_shared_table);
977 writer.dump(&_shared_table, "string");
978 }
979
980 void StringTable::serialize_shared_table_header(SerializeClosure* soc) {
981 _shared_table.serialize_header(soc);
982
983 if (soc->writing()) {
984 // Sanity. Make sure we don't use the shared table at dump time
985 _shared_table.reset();
986 } else if (!AOTMappedHeapLoader::is_in_use()) {
987 _shared_table.reset();
988 }
989 }
990
991 void StringTable::move_shared_strings_into_runtime_table() {
992 precond(CDSConfig::is_dumping_final_static_archive());
993 JavaThread* THREAD = JavaThread::current();
994 HandleMark hm(THREAD);
995
996 int n = 0;
997 _shared_table.iterate_all([&](oop string) {
998 int length = java_lang_String::length(string);
999 Handle h_string (THREAD, string);
1000 StringWrapper name(h_string, length);
1001 unsigned int hash = hash_wrapped_string(name);
1002
1003 assert(!_alt_hash, "too early");
1004 oop interned = do_intern(name, hash, THREAD);
1005 assert(string == interned, "must be");
1006 n++;
1007 });
1008
1009 _shared_table.reset();
1010 log_info(aot)("Moved %d interned strings to runtime table", n);
1011 }
1012 #endif //INCLUDE_CDS_JAVA_HEAP