mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-17 01:24:33 +02:00
8301106: Allow archived Java strings to be moved by GC
Reviewed-by: dholmes
This commit is contained in:
parent
9643f654da
commit
b524a74165
13 changed files with 239 additions and 147 deletions
|
@ -40,6 +40,7 @@
|
|||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "memory/oopFactory.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/access.inline.hpp"
|
||||
#include "oops/compressedOops.hpp"
|
||||
|
@ -72,26 +73,31 @@ const size_t REHASH_LEN = 100;
|
|||
const double CLEAN_DEAD_HIGH_WATER_MARK = 0.5;
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
inline oop read_string_from_compact_hashtable(address base_address, u4 offset) {
|
||||
assert(ArchiveHeapLoader::are_archived_strings_available(), "sanity");
|
||||
if (UseCompressedOops) {
|
||||
assert(sizeof(narrowOop) == sizeof(offset), "must be");
|
||||
narrowOop v = CompressedOops::narrow_oop_cast(offset);
|
||||
return ArchiveHeapLoader::decode_from_archive(v);
|
||||
bool StringTable::_is_two_dimensional_shared_strings_array = false;
|
||||
OopHandle StringTable::_shared_strings_array;
|
||||
int StringTable::_shared_strings_array_root_index;
|
||||
|
||||
inline oop StringTable::read_string_from_compact_hashtable(address base_address, u4 index) {
|
||||
assert(ArchiveHeapLoader::is_in_use(), "sanity");
|
||||
objArrayOop array = (objArrayOop)(_shared_strings_array.resolve());
|
||||
oop s;
|
||||
|
||||
if (!_is_two_dimensional_shared_strings_array) {
|
||||
s = array->obj_at((int)index);
|
||||
} else {
|
||||
assert(!ArchiveHeapLoader::is_loaded(), "Pointer relocation for uncompressed oops is unimplemented");
|
||||
intptr_t dumptime_oop = (uintptr_t)offset;
|
||||
assert(dumptime_oop != 0, "null strings cannot be interned");
|
||||
intptr_t runtime_oop = dumptime_oop +
|
||||
(intptr_t)FileMapInfo::current_info()->header()->heap_begin() +
|
||||
(intptr_t)ArchiveHeapLoader::mapped_heap_delta();
|
||||
return (oop)cast_to_oop(runtime_oop);
|
||||
int primary_index = index >> _secondary_array_index_bits;
|
||||
int secondary_index = index & _secondary_array_index_mask;
|
||||
objArrayOop secondary = (objArrayOop)array->obj_at(primary_index);
|
||||
s = secondary->obj_at(secondary_index);
|
||||
}
|
||||
|
||||
assert(java_lang_String::is_instance(s), "must be");
|
||||
return s;
|
||||
}
|
||||
|
||||
typedef CompactHashtable<
|
||||
const jchar*, oop,
|
||||
read_string_from_compact_hashtable,
|
||||
StringTable::read_string_from_compact_hashtable,
|
||||
java_lang_String::equals> SharedStringTable;
|
||||
|
||||
static SharedStringTable _shared_table;
|
||||
|
@ -224,6 +230,12 @@ void StringTable::create_table() {
|
|||
_local_table = new StringTableHash(start_size_log_2, END_SIZE, REHASH_LEN, true);
|
||||
_oop_storage = OopStorageSet::create_weak("StringTable Weak", mtSymbol);
|
||||
_oop_storage->register_num_dead_callback(&gc_notification);
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
if (ArchiveHeapLoader::is_in_use()) {
|
||||
_shared_strings_array = OopHandle(Universe::vm_global(), HeapShared::get_root(_shared_strings_array_root_index));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t StringTable::item_added() {
|
||||
|
@ -755,49 +767,131 @@ oop StringTable::lookup_shared(const jchar* name, int len) {
|
|||
return _shared_table.lookup(name, java_lang_String::hash_code(name, len), len);
|
||||
}
|
||||
|
||||
class EncodeSharedStringsAsOffsets : StackObj {
|
||||
CompactHashtableWriter* _writer;
|
||||
private:
|
||||
u4 compute_delta(oop s) {
|
||||
HeapWord* start = G1CollectedHeap::heap()->reserved().start();
|
||||
intx offset = ((address)(void*)s) - ((address)(void*)start);
|
||||
assert(offset >= 0, "must be");
|
||||
if (offset > 0xffffffff) {
|
||||
fatal("too large");
|
||||
}
|
||||
return (u4)offset;
|
||||
// This is called BEFORE we enter the CDS safepoint. We can allocate heap objects.
|
||||
// This should be called when we know no more strings will be added (which will be easy
|
||||
// to guarantee because CDS runs with a single Java thread. See JDK-8253495.)
|
||||
void StringTable::allocate_shared_strings_array(TRAPS) {
|
||||
assert(DumpSharedSpaces, "must be");
|
||||
if (_items_count > (size_t)max_jint) {
|
||||
fatal("Too many strings to be archived: " SIZE_FORMAT, _items_count);
|
||||
}
|
||||
public:
|
||||
EncodeSharedStringsAsOffsets(CompactHashtableWriter* writer) : _writer(writer) {}
|
||||
bool do_entry(oop s, bool value_ignored) {
|
||||
assert(s != nullptr, "sanity");
|
||||
assert(!ArchiveHeapWriter::is_string_too_large_to_archive(s), "must be");
|
||||
oop req_s = ArchiveHeapWriter::source_obj_to_requested_obj(s);
|
||||
assert(req_s != nullptr, "must have been archived");
|
||||
unsigned int hash = java_lang_String::hash_code(s);
|
||||
if (UseCompressedOops) {
|
||||
_writer->add(hash, CompressedOops::narrow_oop_value(req_s));
|
||||
} else {
|
||||
_writer->add(hash, compute_delta(req_s));
|
||||
}
|
||||
return true; // keep iterating
|
||||
}
|
||||
};
|
||||
|
||||
// Write the _shared_table (a CompactHashtable) into the CDS archive file.
|
||||
void StringTable::write_shared_table(const DumpedInternedStrings* dumped_interned_strings) {
|
||||
int total = (int)_items_count;
|
||||
size_t single_array_size = objArrayOopDesc::object_size(total);
|
||||
|
||||
log_info(cds)("allocated string table for %d strings", total);
|
||||
|
||||
if (!ArchiveHeapWriter::is_too_large_to_archive(single_array_size)) {
|
||||
// The entire table can fit in a single array
|
||||
objArrayOop array = oopFactory::new_objArray(vmClasses::Object_klass(), total, CHECK);
|
||||
_shared_strings_array = OopHandle(Universe::vm_global(), array);
|
||||
log_info(cds)("string table array (single level) length = %d", total);
|
||||
} else {
|
||||
// Split the table in two levels of arrays.
|
||||
int primary_array_length = (total + _secondary_array_max_length - 1) / _secondary_array_max_length;
|
||||
size_t primary_array_size = objArrayOopDesc::object_size(primary_array_length);
|
||||
size_t secondary_array_size = objArrayOopDesc::object_size(_secondary_array_max_length);
|
||||
|
||||
if (ArchiveHeapWriter::is_too_large_to_archive(secondary_array_size)) {
|
||||
// This can only happen if you have an extremely large number of classes that
|
||||
// refer to more than 16384 * 16384 = 26M interned strings! Not a practical concern
|
||||
// but bail out for safety.
|
||||
log_error(cds)("Too many strings to be archived: " SIZE_FORMAT, _items_count);
|
||||
os::_exit(1);
|
||||
}
|
||||
|
||||
objArrayOop primary = oopFactory::new_objArray(vmClasses::Object_klass(), primary_array_length, CHECK);
|
||||
objArrayHandle primaryHandle(THREAD, primary);
|
||||
_shared_strings_array = OopHandle(Universe::vm_global(), primary);
|
||||
|
||||
log_info(cds)("string table array (primary) length = %d", primary_array_length);
|
||||
for (int i = 0; i < primary_array_length; i++) {
|
||||
int len;
|
||||
if (total > _secondary_array_max_length) {
|
||||
len = _secondary_array_max_length;
|
||||
} else {
|
||||
len = total;
|
||||
}
|
||||
total -= len;
|
||||
|
||||
objArrayOop secondary = oopFactory::new_objArray(vmClasses::Object_klass(), len, CHECK);
|
||||
primaryHandle()->obj_at_put(i, secondary);
|
||||
|
||||
log_info(cds)("string table array (secondary)[%d] length = %d", i, len);
|
||||
assert(!ArchiveHeapWriter::is_too_large_to_archive(secondary), "sanity");
|
||||
}
|
||||
|
||||
assert(total == 0, "must be");
|
||||
_is_two_dimensional_shared_strings_array = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void StringTable::verify_secondary_array_index_bits() {
|
||||
int max;
|
||||
for (max = 1; ; max++) {
|
||||
size_t next_size = objArrayOopDesc::object_size(1 << (max + 1));
|
||||
if (ArchiveHeapWriter::is_too_large_to_archive(next_size)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Currently max is 17 for +UseCompressedOops, 16 for -UseCompressedOops.
|
||||
// When we add support for Shenandoah (which has a smaller mininum region size than G1),
|
||||
// max will become 15/14.
|
||||
//
|
||||
// We use _secondary_array_index_bits==14 as that will be the eventual value, and will
|
||||
// make testing easier.
|
||||
assert(_secondary_array_index_bits <= max,
|
||||
"_secondary_array_index_bits (%d) must be smaller than max possible value (%d)",
|
||||
_secondary_array_index_bits, max);
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
||||
// This is called AFTER we enter the CDS safepoint.
|
||||
//
|
||||
// For each shared string:
|
||||
// [1] Store it into _shared_strings_array. Encode its position as a 32-bit index.
|
||||
// [2] Store the index and hashcode into _shared_table.
|
||||
oop StringTable::init_shared_table(const DumpedInternedStrings* dumped_interned_strings) {
|
||||
assert(HeapShared::can_write(), "must be");
|
||||
objArrayOop array = (objArrayOop)(_shared_strings_array.resolve());
|
||||
|
||||
verify_secondary_array_index_bits();
|
||||
|
||||
_shared_table.reset();
|
||||
CompactHashtableWriter writer(_items_count, ArchiveBuilder::string_stats());
|
||||
|
||||
// Encode the strings in the CompactHashtable using offsets -- we know that the
|
||||
// strings will not move during runtime because they are inside the G1 closed
|
||||
// archive region.
|
||||
EncodeSharedStringsAsOffsets offset_finder(&writer);
|
||||
dumped_interned_strings->iterate(&offset_finder);
|
||||
int index = 0;
|
||||
auto copy_into_array = [&] (oop string, bool value_ignored) {
|
||||
unsigned int hash = java_lang_String::hash_code(string);
|
||||
writer.add(hash, index);
|
||||
|
||||
if (!_is_two_dimensional_shared_strings_array) {
|
||||
assert(index < array->length(), "no strings should have been added");
|
||||
array->obj_at_put(index, string);
|
||||
} else {
|
||||
int primary_index = index >> _secondary_array_index_bits;
|
||||
int secondary_index = index & _secondary_array_index_mask;
|
||||
|
||||
assert(primary_index < array->length(), "no strings should have been added");
|
||||
objArrayOop secondary = (objArrayOop)array->obj_at(primary_index);
|
||||
|
||||
assert(secondary != nullptr && secondary->is_objArray(), "must be");
|
||||
assert(secondary_index < secondary->length(), "no strings should have been added");
|
||||
secondary->obj_at_put(secondary_index, string);
|
||||
}
|
||||
|
||||
index ++;
|
||||
};
|
||||
dumped_interned_strings->iterate_all(copy_into_array);
|
||||
|
||||
writer.dump(&_shared_table, "string");
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
void StringTable::set_shared_strings_array_index(int root_index) {
|
||||
_shared_strings_array_root_index = root_index;
|
||||
}
|
||||
|
||||
void StringTable::serialize_shared_table_header(SerializeClosure* soc) {
|
||||
|
@ -806,47 +900,11 @@ void StringTable::serialize_shared_table_header(SerializeClosure* soc) {
|
|||
if (soc->writing()) {
|
||||
// Sanity. Make sure we don't use the shared table at dump time
|
||||
_shared_table.reset();
|
||||
} else if (!ArchiveHeapLoader::are_archived_strings_available()) {
|
||||
} else if (!ArchiveHeapLoader::is_in_use()) {
|
||||
_shared_table.reset();
|
||||
}
|
||||
|
||||
soc->do_bool(&_is_two_dimensional_shared_strings_array);
|
||||
soc->do_u4((u4*)(&_shared_strings_array_root_index));
|
||||
}
|
||||
|
||||
class SharedStringTransfer {
|
||||
JavaThread* _current;
|
||||
public:
|
||||
SharedStringTransfer(JavaThread* current) : _current(current) {}
|
||||
|
||||
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();
|
||||
|
||||
SharedStringTransfer transfer(THREAD);
|
||||
shared_table_copy.iterate(&transfer);
|
||||
}
|
||||
|
||||
#endif //INCLUDE_CDS_JAVA_HEAP
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue