/* * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// We prefer short chains of avg 2 constdouble PREF_AVG_LIST_LEN = 2.0; // 2^24 is max size const size_t END_SIZE = 24; // If a chain gets to 100 something might be wrong const size_t REHASH_LEN = 100; // If we have as many dead items as 50% of the number of bucket constdouble CLEAN_DEAD_HIGH_WATER_MARK = 0.5;
class StringTableConfig : public StackObj { private: public: typedef WeakHandle Value;
static uintx get_hash(Value const& value, bool* is_dead) {
oop val_oop = value.peek(); if (val_oop == NULL) {
*is_dead = true; return 0;
}
*is_dead = false;
ResourceMark rm; // All String oops are hashed as unicode int length;
jchar* chars = java_lang_String::as_unicode_string_or_null(val_oop, length); if (chars != NULL) { return hash_string(chars, length, _alt_hash);
}
vm_exit_out_of_memory(length, OOM_MALLOC_ERROR, "get hash from oop"); return 0;
} // We use default allocation/deallocation but counted staticvoid* allocate_node(void* context, size_t size, Value const& value) {
StringTable::item_added(); return AllocateHeap(size, mtSymbol);
} staticvoid free_node(void* context, void* memory, Value const& value) {
value.release(StringTable::_oop_storage);
FreeHeap(memory);
StringTable::item_removed();
}
};
class StringTableLookupJchar : StackObj { private:
Thread* _thread;
uintx _hash; int _len; const jchar* _str;
Handle _found;
public:
StringTableLookupJchar(Thread* thread, uintx hash, const jchar* key, int len)
: _thread(thread), _hash(hash), _len(len), _str(key) {
}
uintx get_hash() const { return _hash;
} bool equals(WeakHandle* value, bool* is_dead) {
oop val_oop = value->peek(); if (val_oop == NULL) { // dead oop, mark this hash dead for cleaning
*is_dead = true; returnfalse;
} bool equals = java_lang_String::equals(val_oop, _str, _len); if (!equals) { returnfalse;
} // Need to resolve weak handle and Handleize through possible safepoint.
_found = Handle(_thread, value->resolve()); returntrue;
}
};
class StringTableLookupOop : public StackObj { private:
Thread* _thread;
uintx _hash;
Handle _find;
Handle _found; // Might be a different oop with the same value that's already // in the table, which is the point. public:
StringTableLookupOop(Thread* thread, uintx hash, Handle handle)
: _thread(thread), _hash(hash), _find(handle) { }
uintx get_hash() const { return _hash;
}
bool equals(WeakHandle* value, bool* is_dead) {
oop val_oop = value->peek(); if (val_oop == NULL) { // dead oop, mark this hash dead for cleaning
*is_dead = true; returnfalse;
} bool equals = java_lang_String::equals(_find(), val_oop); if (!equals) { returnfalse;
} // Need to resolve weak handle and Handleize through possible safepoint.
_found = Handle(_thread, value->resolve()); returntrue;
}
};
assert(java_lang_String::equals(string_h(), name, len), "string must be properly initialized");
assert(len == java_lang_String::length(string_h()), "Must be same length");
// Notify deduplication support that the string is being interned. A string // must never be deduplicated after it has been interned. Doing so interferes // with compiler optimizations done on e.g. interned string literals. if (StringDedup::is_enabled()) {
StringDedup::notify_intern(string_h());
}
bool rehash_warning; do { // Callers have already looked up the String using the jchar* name, so just go to add.
WeakHandle wh(_oop_storage, string_h); // The hash table takes ownership of the WeakHandle, even if it's not inserted. if (_local_table->insert(THREAD, lookup, wh, &rehash_warning)) {
update_needs_rehash(rehash_warning); return wh.resolve();
} // In case another thread did a concurrent add, return value already in the table. // This could fail if the String got gc'ed concurrently, so loop back until success. if (_local_table->get(THREAD, lookup, stg, &rehash_warning)) {
update_needs_rehash(rehash_warning); return stg.get_res_oop();
}
} while(true);
}
// Concurrent work void StringTable::grow(JavaThread* jt) {
StringTableHash::GrowTask gt(_local_table); if (!gt.prepare(jt)) { return;
}
log_trace(stringtable)("Started to grow");
{
TraceTime timer("Grow", TRACETIME_LOG(Debug, stringtable, perf)); while (gt.do_task(jt)) {
gt.pause(jt);
{
ThreadBlockInVM tbivm(jt);
}
gt.cont(jt);
}
}
gt.done(jt);
_current_size = table_size();
log_debug(stringtable)("Grown to size:" SIZE_FORMAT, _current_size);
}
double load_factor = StringTable::get_load_factor(); double dead_factor = StringTable::get_dead_factor(num_dead); // 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_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",
load_factor, dead_factor);
trigger_concurrent_work();
}
}
void StringTable::do_concurrent_work(JavaThread* jt) { 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 (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
grow(jt);
} else {
clean_dead_entries(jt);
}
Atomic::release_store(&_has_work, false);
}
// Rehash bool StringTable::do_rehash() { if (!_local_table->is_safepoint_safe()) { returnfalse;
}
// We use current size, not max size.
size_t new_size = _local_table->get_size_log2(Thread::current());
StringTableHash* new_table = new StringTableHash(new_size, END_SIZE, REHASH_LEN, true); // Use alt hash from now on
_alt_hash = true; if (!_local_table->try_move_nodes_to(Thread::current(), new_table)) {
_alt_hash = false; delete new_table; returnfalse;
}
// free old table delete _local_table;
_local_table = new_table;
if (obj->klass() == vmClasses::String_klass()) { // This may overcount if String.value arrays are shared.
word_size += java_lang_String::value(obj)->size();
}
return word_size * HeapWordSize;
}
struct SizeFunc : StackObj {
size_t operator()(WeakHandle* val) {
oop s = val->peek(); if (s == NULL) { // Dead return 0;
} return literal_size(s);
};
};
// Verification class VerifyStrings : StackObj { public: booloperator()(WeakHandle* val) {
oop s = val->peek(); if (s != NULL) {
assert(java_lang_String::length(s) >= 0, "Length on string must work.");
} returntrue;
};
};
// This verification is part of Universe::verify() and needs to be quick. void StringTable::verify() {
VerifyStrings vs;
_local_table->do_safepoint_scan(vs);
}
// Verification and comp class VerifyCompStrings : StackObj { staticunsigned string_hash(oop const& str) { return java_lang_String::hash_code_noupdate(str);
} staticbool string_equals(oop const& a, oop const& b) { return java_lang_String::equals(a, b);
}
// Utility for dumping strings
StringtableDCmd::StringtableDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
_verbose("-verbose", "Dump the content of each string in the table", "BOOLEAN", false, "false") {
_dcmdparser.add_dcmd_option(&_verbose);
}
oop StringTable::create_archived_string(oop s) {
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
assert(java_lang_String::is_instance(s), "sanity");
assert(!HeapShared::is_archived_object_during_dumptime(s), "sanity");
// adjust the pointer to the 'value' field in the new String oop
java_lang_String::set_value_raw(new_s, new_v); // Prevent string deduplication from changing the 'value' field to // something not in the archive before building the archive. Also marks // the shared string when loaded.
java_lang_String::set_deduplication_forbidden(new_s); return new_s;
}
// Copy the interned strings into the "string space" within the java heap
CopyToArchive copier(&writer);
dumped_interned_strings->iterate(&copier);
writer.dump(&_shared_table, "string");
}
if (soc->writing()) { // Sanity. Make sure we don't use the shared table at dump time
_shared_table.reset();
} elseif (!ArchiveHeapLoader::are_archived_strings_available()) {
_shared_table.reset();
}
void do_value(oop string) {
JavaThread* THREAD = _current;
ExceptionMark rm(THREAD);
HandleMark hm(THREAD);
StringTable::intern(string, THREAD); if (HAS_PENDING_EXCEPTION) { // The archived constant pools contains strings that must be in the interned string table. // If we fail here, it means the VM runs out of memory during bootstrap, so there's no point // of trying to recover from here.
vm_exit_during_initialization("Failed to transfer shared strings to interned string table");
}
}
};
// If the CDS archive heap is loaded (not mapped) into the old generation, // it's possible for the shared strings to move due to full GC, making the // _shared_table invalid. Therefore, we proactively copy all the shared // strings into the _local_table, which can deal with oop relocation. void StringTable::transfer_shared_strings_to_local_table() {
assert(ArchiveHeapLoader::is_loaded(), "must be");
EXCEPTION_MARK;
// Reset _shared_table so that during the transfer, StringTable::intern() // will not look up from there. Instead, it will create a new entry in // _local_table for each element in shared_table_copy.
SharedStringTable shared_table_copy = _shared_table;
_shared_table.reset();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.