8179302: Pre-resolve constant pool string entries and cache resolved_reference arrays in CDS archive

8185924: G1NoteEndOfConcMarkClosure::doHeapRegion() does not do remembered set cleanup work for archive region

Shared class' constant pool resolved_references array is cached.

Co-authored-by: Thomas Schatzl <thomas.schatzl@oracle.com>
Reviewed-by: coleenp, iklam, tschatzl
This commit is contained in:
Jiangli Zhou 2017-08-14 14:32:17 -04:00
parent 85fedbbb12
commit 4a77945c89
36 changed files with 931 additions and 418 deletions

View file

@ -90,7 +90,7 @@ class StableMemoryChecker : public StackObj {
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
StringTable* StringTable::_the_table = NULL; StringTable* StringTable::_the_table = NULL;
bool StringTable::_ignore_shared_strings = false; bool StringTable::_shared_string_mapped = false;
bool StringTable::_needs_rehashing = false; bool StringTable::_needs_rehashing = false;
volatile int StringTable::_parallel_claimed_idx = 0; volatile int StringTable::_parallel_claimed_idx = 0;
@ -678,13 +678,30 @@ int StringtableDCmd::num_arguments() {
} }
} }
#if INCLUDE_CDS_JAVA_HEAP
// Sharing // Sharing
oop StringTable::create_archived_string(oop s, Thread* THREAD) {
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
oop new_s = NULL;
typeArrayOop v = java_lang_String::value(s);
typeArrayOop new_v = (typeArrayOop)MetaspaceShared::archive_heap_object(v, THREAD);
if (new_v == NULL) {
return NULL;
}
new_s = MetaspaceShared::archive_heap_object(s, THREAD);
if (new_s == NULL) {
return NULL;
}
// adjust the pointer to the 'value' field in the new String oop
java_lang_String::set_value_raw(new_s, new_v);
return new_s;
}
bool StringTable::copy_shared_string(GrowableArray<MemRegion> *string_space, bool StringTable::copy_shared_string(GrowableArray<MemRegion> *string_space,
CompactStringTableWriter* writer) { CompactStringTableWriter* writer) {
#if INCLUDE_CDS && INCLUDE_ALL_GCS && defined(_LP64) && !defined(_WINDOWS) assert(MetaspaceShared::is_heap_object_archiving_allowed(), "must be");
assert(UseG1GC, "Only support G1 GC");
assert(UseCompressedOops && UseCompressedClassPointers,
"Only support UseCompressedOops and UseCompressedClassPointers enabled");
Thread* THREAD = Thread::current(); Thread* THREAD = Thread::current();
G1CollectedHeap::heap()->begin_archive_alloc_range(); G1CollectedHeap::heap()->begin_archive_alloc_range();
@ -697,64 +714,29 @@ bool StringTable::copy_shared_string(GrowableArray<MemRegion> *string_space,
continue; continue;
} }
// allocate the new 'value' array first java_lang_String::set_hash(s, hash);
typeArrayOop v = java_lang_String::value(s); oop new_s = create_archived_string(s, THREAD);
int v_len = v->size();
typeArrayOop new_v;
if (G1CollectedHeap::heap()->is_archive_alloc_too_large(v_len)) {
continue; // skip the current String. The 'value' array is too large to handle
} else {
new_v = (typeArrayOop)G1CollectedHeap::heap()->archive_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()->archive_mem_allocate(s_len);
if (new_s == NULL) { if (new_s == NULL) {
return false; continue;
} }
s->identity_hash(); // set the archived string in bucket
v->identity_hash(); bucket->set_literal(new_s);
// 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 // add to the compact table
writer->add(hash, new_s); writer->add(hash, new_s);
MetaspaceShared::relocate_klass_ptr(new_s);
MetaspaceShared::relocate_klass_ptr(new_v);
} }
} }
G1CollectedHeap::heap()->end_archive_alloc_range(string_space, os::vm_allocation_granularity()); G1CollectedHeap::heap()->end_archive_alloc_range(string_space, os::vm_allocation_granularity());
assert(string_space->length() <= 2, "sanity"); assert(string_space->length() <= 2, "sanity");
#endif
return true; return true;
} }
void StringTable::write_to_archive(GrowableArray<MemRegion> *string_space) { void StringTable::write_to_archive(GrowableArray<MemRegion> *string_space) {
#if INCLUDE_CDS assert(MetaspaceShared::is_heap_object_archiving_allowed(), "must be");
_shared_table.reset(); _shared_table.reset();
if (!(UseG1GC && UseCompressedOops && UseCompressedClassPointers)) {
log_info(cds)(
"Shared strings are excluded from the archive as UseG1GC, "
"UseCompressedOops and UseCompressedClassPointers are required."
"Current settings: UseG1GC=%s, UseCompressedOops=%s, UseCompressedClassPointers=%s.",
BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedOops),
BOOL_TO_STR(UseCompressedClassPointers));
} else {
int num_buckets = the_table()->number_of_entries() / int num_buckets = the_table()->number_of_entries() /
SharedSymbolTableBucketSize; SharedSymbolTableBucketSize;
// calculation of num_buckets can result in zero buckets, we need at least one // calculation of num_buckets can result in zero buckets, we need at least one
@ -765,26 +747,21 @@ void StringTable::write_to_archive(GrowableArray<MemRegion> *string_space) {
if (copy_shared_string(string_space, &writer)) { if (copy_shared_string(string_space, &writer)) {
writer.dump(&_shared_table); writer.dump(&_shared_table);
} }
}
#endif
} }
void StringTable::serialize(SerializeClosure* soc) { void StringTable::serialize(SerializeClosure* soc) {
#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
_shared_table.set_type(CompactHashtable<oop, char>::_string_table); _shared_table.set_type(CompactHashtable<oop, char>::_string_table);
_shared_table.serialize(soc); _shared_table.serialize(soc);
if (soc->writing()) { if (soc->writing()) {
_shared_table.reset(); // Sanity. Make sure we don't use the shared table at dump time _shared_table.reset(); // Sanity. Make sure we don't use the shared table at dump time
} else if (_ignore_shared_strings) { } else if (!_shared_string_mapped) {
_shared_table.reset(); _shared_table.reset();
} }
#endif
} }
void StringTable::shared_oops_do(OopClosure* f) { void StringTable::shared_oops_do(OopClosure* f) {
#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
_shared_table.oops_do(f); _shared_table.oops_do(f);
#endif
} }
#endif //INCLUDE_CDS_JAVA_HEAP

View file

@ -43,7 +43,7 @@ private:
// Shared string table // Shared string table
static CompactHashtable<oop, char> _shared_table; static CompactHashtable<oop, char> _shared_table;
static bool _ignore_shared_strings; static bool _shared_string_mapped;
// Set if one bucket is out of balance due to hash algorithm deficiency // Set if one bucket is out of balance due to hash algorithm deficiency
static bool _needs_rehashing; static bool _needs_rehashing;
@ -157,13 +157,14 @@ public:
static int verify_and_compare_entries(); static int verify_and_compare_entries();
// Sharing // Sharing
static void ignore_shared_strings(bool v) { _ignore_shared_strings = v; } static void set_shared_string_mapped() { _shared_string_mapped = true; }
static bool shared_string_ignored() { return _ignore_shared_strings; } static bool shared_string_mapped() { return _shared_string_mapped; }
static void shared_oops_do(OopClosure* f); static void shared_oops_do(OopClosure* f) NOT_CDS_JAVA_HEAP_RETURN;
static bool copy_shared_string(GrowableArray<MemRegion> *string_space, static bool copy_shared_string(GrowableArray<MemRegion> *string_space,
CompactStringTableWriter* ch_table); CompactStringTableWriter* ch_table) NOT_CDS_JAVA_HEAP_RETURN_(false);
static void write_to_archive(GrowableArray<MemRegion> *string_space); static oop create_archived_string(oop s, Thread* THREAD) NOT_CDS_JAVA_HEAP_RETURN_(NULL);
static void serialize(SerializeClosure* soc); static void write_to_archive(GrowableArray<MemRegion> *string_space) NOT_CDS_JAVA_HEAP_RETURN;
static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
// Rehash the symbol table if it gets out of balance // Rehash the symbol table if it gets out of balance
static void rehash_table(); static void rehash_table();

View file

@ -331,12 +331,13 @@ void G1DefaultPLABAllocator::waste(size_t& wasted, size_t& undo_wasted) {
} }
bool G1ArchiveAllocator::_archive_check_enabled = false; bool G1ArchiveAllocator::_archive_check_enabled = false;
G1ArchiveRegionMap G1ArchiveAllocator::_archive_region_map; G1ArchiveRegionMap G1ArchiveAllocator::_closed_archive_region_map;
G1ArchiveRegionMap G1ArchiveAllocator::_open_archive_region_map;
G1ArchiveAllocator* G1ArchiveAllocator::create_allocator(G1CollectedHeap* g1h) { G1ArchiveAllocator* G1ArchiveAllocator::create_allocator(G1CollectedHeap* g1h, bool open) {
// Create the archive allocator, and also enable archive object checking // Create the archive allocator, and also enable archive object checking
// in mark-sweep, since we will be creating archive regions. // in mark-sweep, since we will be creating archive regions.
G1ArchiveAllocator* result = new G1ArchiveAllocator(g1h); G1ArchiveAllocator* result = new G1ArchiveAllocator(g1h, open);
enable_archive_object_check(); enable_archive_object_check();
return result; return result;
} }
@ -350,7 +351,11 @@ bool G1ArchiveAllocator::alloc_new_region() {
return false; return false;
} }
assert(hr->is_empty(), "expected empty region (index %u)", hr->hrm_index()); assert(hr->is_empty(), "expected empty region (index %u)", hr->hrm_index());
hr->set_archive(); if (_open) {
hr->set_open_archive();
} else {
hr->set_closed_archive();
}
_g1h->old_set_add(hr); _g1h->old_set_add(hr);
_g1h->hr_printer()->alloc(hr); _g1h->hr_printer()->alloc(hr);
_allocated_regions.append(hr); _allocated_regions.append(hr);
@ -362,7 +367,7 @@ bool G1ArchiveAllocator::alloc_new_region() {
_max = _bottom + HeapRegion::min_region_size_in_words(); _max = _bottom + HeapRegion::min_region_size_in_words();
// Tell mark-sweep that objects in this region are not to be marked. // Tell mark-sweep that objects in this region are not to be marked.
set_range_archive(MemRegion(_bottom, HeapRegion::GrainWords), true); set_range_archive(MemRegion(_bottom, HeapRegion::GrainWords), _open);
// Since we've modified the old set, call update_sizes. // Since we've modified the old set, call update_sizes.
_g1h->g1mm()->update_sizes(); _g1h->g1mm()->update_sizes();

View file

@ -321,11 +321,19 @@ protected:
}; };
// G1ArchiveAllocator is used to allocate memory in archive // G1ArchiveAllocator is used to allocate memory in archive
// regions. Such regions are not modifiable by GC, being neither // regions. Such regions are not scavenged nor compacted by GC.
// scavenged nor compacted, or even marked in the object header. // There are two types of archive regions, which are
// They can contain no pointers to non-archive heap regions, // differ in the kind of references allowed for the contained objects:
//
// - 'Closed' archive region contain no references outside of other
// closed archive regions. The region is immutable by GC. GC does
// not mark object header in 'closed' archive region.
// - An 'open' archive region allow references to any other regions,
// including closed archive, open archive and other java heap regions.
// GC can adjust pointers and mark object header in 'open' archive region.
class G1ArchiveAllocator : public CHeapObj<mtGC> { class G1ArchiveAllocator : public CHeapObj<mtGC> {
protected: protected:
bool _open; // Indicate if the region is 'open' archive.
G1CollectedHeap* _g1h; G1CollectedHeap* _g1h;
// The current allocation region // The current allocation region
@ -347,7 +355,7 @@ protected:
bool alloc_new_region(); bool alloc_new_region();
public: public:
G1ArchiveAllocator(G1CollectedHeap* g1h) : G1ArchiveAllocator(G1CollectedHeap* g1h, bool open) :
_g1h(g1h), _g1h(g1h),
_allocation_region(NULL), _allocation_region(NULL),
_allocated_regions((ResourceObj::set_allocation_type((address) &_allocated_regions, _allocated_regions((ResourceObj::set_allocation_type((address) &_allocated_regions,
@ -356,13 +364,14 @@ public:
_summary_bytes_used(0), _summary_bytes_used(0),
_bottom(NULL), _bottom(NULL),
_top(NULL), _top(NULL),
_max(NULL) { } _max(NULL),
_open(open) { }
virtual ~G1ArchiveAllocator() { virtual ~G1ArchiveAllocator() {
assert(_allocation_region == NULL, "_allocation_region not NULL"); assert(_allocation_region == NULL, "_allocation_region not NULL");
} }
static G1ArchiveAllocator* create_allocator(G1CollectedHeap* g1h); static G1ArchiveAllocator* create_allocator(G1CollectedHeap* g1h, bool open);
// Allocate memory for an individual object. // Allocate memory for an individual object.
HeapWord* archive_mem_allocate(size_t word_size); HeapWord* archive_mem_allocate(size_t word_size);
@ -389,18 +398,26 @@ public:
static inline void enable_archive_object_check(); static inline void enable_archive_object_check();
// Set the regions containing the specified address range as archive/non-archive. // Set the regions containing the specified address range as archive/non-archive.
static inline void set_range_archive(MemRegion range, bool is_archive); static inline void set_range_archive(MemRegion range, bool open);
// Check if the object is in closed archive
static inline bool is_closed_archive_object(oop object);
// Check if the object is in open archive
static inline bool is_open_archive_object(oop object);
// Check if the object is either in closed archive or open archive
static inline bool is_archive_object(oop object); static inline bool is_archive_object(oop object);
private: private:
static bool _archive_check_enabled; static bool _archive_check_enabled;
static G1ArchiveRegionMap _archive_region_map; static G1ArchiveRegionMap _closed_archive_region_map;
static G1ArchiveRegionMap _open_archive_region_map;
// Check if an object is in an archive region using the _archive_region_map. // Check if an object is in a closed archive region using the _closed_archive_region_map.
static inline bool in_archive_range(oop object); static inline bool in_closed_archive_range(oop object);
// Check if an object is in open archive region using the _open_archive_region_map.
static inline bool in_open_archive_range(oop object);
// Check if archive object checking is enabled, to avoid calling in_archive_range // Check if archive object checking is enabled, to avoid calling in_open/closed_archive_range
// unnecessarily. // unnecessarily.
static inline bool archive_check_enabled(); static inline bool archive_check_enabled();
}; };

View file

@ -55,37 +55,60 @@ inline HeapWord* G1PLABAllocator::plab_allocate(InCSetState dest,
} }
} }
// Create the _archive_region_map which is used to identify archive objects. // Create the maps which is used to identify archive objects.
inline void G1ArchiveAllocator::enable_archive_object_check() { inline void G1ArchiveAllocator::enable_archive_object_check() {
assert(!_archive_check_enabled, "archive range check already enabled"); if (_archive_check_enabled) {
return;
}
_archive_check_enabled = true; _archive_check_enabled = true;
size_t length = Universe::heap()->max_capacity(); size_t length = Universe::heap()->max_capacity();
_archive_region_map.initialize((HeapWord*)Universe::heap()->base(), _closed_archive_region_map.initialize((HeapWord*)Universe::heap()->base(),
(HeapWord*)Universe::heap()->base() + length,
HeapRegion::GrainBytes);
_open_archive_region_map.initialize((HeapWord*)Universe::heap()->base(),
(HeapWord*)Universe::heap()->base() + length, (HeapWord*)Universe::heap()->base() + length,
HeapRegion::GrainBytes); HeapRegion::GrainBytes);
} }
// Set the regions containing the specified address range as archive/non-archive. // Set the regions containing the specified address range as archive.
inline void G1ArchiveAllocator::set_range_archive(MemRegion range, bool is_archive) { inline void G1ArchiveAllocator::set_range_archive(MemRegion range, bool open) {
assert(_archive_check_enabled, "archive range check not enabled"); assert(_archive_check_enabled, "archive range check not enabled");
_archive_region_map.set_by_address(range, is_archive); if (open) {
_open_archive_region_map.set_by_address(range, true);
} else {
_closed_archive_region_map.set_by_address(range, true);
}
} }
// Check if an object is in an archive region using the _archive_region_map. // Check if an object is in a closed archive region using the _archive_region_map.
inline bool G1ArchiveAllocator::in_archive_range(oop object) { inline bool G1ArchiveAllocator::in_closed_archive_range(oop object) {
// This is the out-of-line part of is_archive_object test, done separately // This is the out-of-line part of is_closed_archive_object test, done separately
// to avoid additional performance impact when the check is not enabled. // to avoid additional performance impact when the check is not enabled.
return _archive_region_map.get_by_address((HeapWord*)object); return _closed_archive_region_map.get_by_address((HeapWord*)object);
} }
// Check if archive object checking is enabled, to avoid calling in_archive_range inline bool G1ArchiveAllocator::in_open_archive_range(oop object) {
return _open_archive_region_map.get_by_address((HeapWord*)object);
}
// Check if archive object checking is enabled, to avoid calling in_open/closed_archive_range
// unnecessarily. // unnecessarily.
inline bool G1ArchiveAllocator::archive_check_enabled() { inline bool G1ArchiveAllocator::archive_check_enabled() {
return _archive_check_enabled; return _archive_check_enabled;
} }
inline bool G1ArchiveAllocator::is_closed_archive_object(oop object) {
return (archive_check_enabled() && in_closed_archive_range(object));
}
inline bool G1ArchiveAllocator::is_open_archive_object(oop object) {
return (archive_check_enabled() && in_open_archive_range(object));
}
inline bool G1ArchiveAllocator::is_archive_object(oop object) { inline bool G1ArchiveAllocator::is_archive_object(oop object) {
return (archive_check_enabled() && in_archive_range(object)); return (archive_check_enabled() && (in_closed_archive_range(object) ||
in_open_archive_range(object)));
} }
#endif // SHARE_VM_GC_G1_G1ALLOCATOR_HPP #endif // SHARE_VM_GC_G1_G1ALLOCATOR_HPP

View file

@ -614,10 +614,10 @@ HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size,
return NULL; return NULL;
} }
void G1CollectedHeap::begin_archive_alloc_range() { void G1CollectedHeap::begin_archive_alloc_range(bool open) {
assert_at_safepoint(true /* should_be_vm_thread */); assert_at_safepoint(true /* should_be_vm_thread */);
if (_archive_allocator == NULL) { if (_archive_allocator == NULL) {
_archive_allocator = G1ArchiveAllocator::create_allocator(this); _archive_allocator = G1ArchiveAllocator::create_allocator(this, open);
} }
} }
@ -661,7 +661,9 @@ bool G1CollectedHeap::check_archive_addresses(MemRegion* ranges, size_t count) {
return true; return true;
} }
bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, size_t count) { bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges,
size_t count,
bool open) {
assert(!is_init_completed(), "Expect to be called at JVM init time"); assert(!is_init_completed(), "Expect to be called at JVM init time");
assert(ranges != NULL, "MemRegion array NULL"); assert(ranges != NULL, "MemRegion array NULL");
assert(count != 0, "No MemRegions provided"); assert(count != 0, "No MemRegions provided");
@ -680,8 +682,8 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, size_t count) {
G1ArchiveAllocator::enable_archive_object_check(); G1ArchiveAllocator::enable_archive_object_check();
// For each specified MemRegion range, allocate the corresponding G1 // For each specified MemRegion range, allocate the corresponding G1
// regions and mark them as archive regions. We expect the ranges in // regions and mark them as archive regions. We expect the ranges
// ascending starting address order, without overlap. // in ascending starting address order, without overlap.
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
MemRegion curr_range = ranges[i]; MemRegion curr_range = ranges[i];
HeapWord* start_address = curr_range.start(); HeapWord* start_address = curr_range.start();
@ -726,8 +728,8 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, size_t count) {
} }
// Mark each G1 region touched by the range as archive, add it to the old set, // Mark each G1 region touched by the range as archive, add it to
// and set the allocation context and top. // the old set, and set the allocation context and top.
HeapRegion* curr_region = _hrm.addr_to_region(start_address); HeapRegion* curr_region = _hrm.addr_to_region(start_address);
HeapRegion* last_region = _hrm.addr_to_region(last_address); HeapRegion* last_region = _hrm.addr_to_region(last_address);
prev_last_region = last_region; prev_last_region = last_region;
@ -736,20 +738,30 @@ bool G1CollectedHeap::alloc_archive_regions(MemRegion* ranges, size_t count) {
assert(curr_region->is_empty() && !curr_region->is_pinned(), assert(curr_region->is_empty() && !curr_region->is_pinned(),
"Region already in use (index %u)", curr_region->hrm_index()); "Region already in use (index %u)", curr_region->hrm_index());
curr_region->set_allocation_context(AllocationContext::system()); curr_region->set_allocation_context(AllocationContext::system());
curr_region->set_archive(); if (open) {
curr_region->set_open_archive();
} else {
curr_region->set_closed_archive();
}
_hr_printer.alloc(curr_region); _hr_printer.alloc(curr_region);
_old_set.add(curr_region); _old_set.add(curr_region);
HeapWord* top;
HeapRegion* next_region;
if (curr_region != last_region) { if (curr_region != last_region) {
curr_region->set_top(curr_region->end()); top = curr_region->end();
curr_region = _hrm.next_region_in_heap(curr_region); next_region = _hrm.next_region_in_heap(curr_region);
} else { } else {
curr_region->set_top(last_address + 1); top = last_address + 1;
curr_region = NULL; next_region = NULL;
} }
curr_region->set_top(top);
curr_region->set_first_dead(top);
curr_region->set_end_of_live(top);
curr_region = next_region;
} }
// Notify mark-sweep of the archive range. // Notify mark-sweep of the archive
G1ArchiveAllocator::set_range_archive(curr_range, true); G1ArchiveAllocator::set_range_archive(curr_range, open);
} }
return true; return true;
} }
@ -5223,12 +5235,8 @@ public:
// We ignore humongous regions. We left the humongous set unchanged. // We ignore humongous regions. We left the humongous set unchanged.
} else { } else {
assert(r->is_young() || r->is_free() || r->is_old(), "invariant"); assert(r->is_young() || r->is_free() || r->is_old(), "invariant");
// We now consider all regions old, so register as such. Leave // We now move all (non-humongous, non-old) regions to old gen, and register them as such.
// archive regions set that way, however, while still adding r->move_to_old();
// them to the old set.
if (!r->is_archive()) {
r->set_old();
}
_old_set->add(r); _old_set->add(r);
} }
_total_used += r->used(); _total_used += r->used();

View file

@ -681,7 +681,7 @@ public:
// VM thread at safepoints, without the heap lock held. They can be used // VM thread at safepoints, without the heap lock held. They can be used
// to create and archive a set of heap regions which can be mapped at the // to create and archive a set of heap regions which can be mapped at the
// same fixed addresses in a subsequent JVM invocation. // same fixed addresses in a subsequent JVM invocation.
void begin_archive_alloc_range(); void begin_archive_alloc_range(bool open = false);
// Check if the requested size would be too large for an archive allocation. // Check if the requested size would be too large for an archive allocation.
bool is_archive_alloc_too_large(size_t word_size); bool is_archive_alloc_too_large(size_t word_size);
@ -706,7 +706,7 @@ public:
// Commit the appropriate G1 regions containing the specified MemRegions // Commit the appropriate G1 regions containing the specified MemRegions
// and mark them as 'archive' regions. The regions in the array must be // and mark them as 'archive' regions. The regions in the array must be
// non-overlapping and in order of ascending address. // non-overlapping and in order of ascending address.
bool alloc_archive_regions(MemRegion* range, size_t count); bool alloc_archive_regions(MemRegion* range, size_t count, bool open);
// Insert any required filler objects in the G1 regions around the specified // Insert any required filler objects in the G1 regions around the specified
// ranges to make the regions parseable. This must be called after // ranges to make the regions parseable. This must be called after

View file

@ -1134,13 +1134,10 @@ public:
const uint humongous_regions_removed() { return _humongous_regions_removed; } const uint humongous_regions_removed() { return _humongous_regions_removed; }
bool doHeapRegion(HeapRegion *hr) { bool doHeapRegion(HeapRegion *hr) {
if (hr->is_archive()) {
return false;
}
_g1->reset_gc_time_stamps(hr); _g1->reset_gc_time_stamps(hr);
hr->note_end_of_marking(); hr->note_end_of_marking();
if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young()) { if (hr->used() > 0 && hr->max_live_bytes() == 0 && !hr->is_young() && !hr->is_archive()) {
_freed_bytes += hr->used(); _freed_bytes += hr->used();
hr->set_containing_set(NULL); hr->set_containing_set(NULL);
if (hr->is_humongous()) { if (hr->is_humongous()) {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -37,7 +37,9 @@ class G1HeapRegionTraceType : AllStatic {
StartsHumongous, StartsHumongous,
ContinuesHumongous, ContinuesHumongous,
Old, Old,
Archive, Pinned,
OpenArchive,
ClosedArchive,
G1HeapRegionTypeEndSentinel G1HeapRegionTypeEndSentinel
}; };
@ -49,7 +51,9 @@ class G1HeapRegionTraceType : AllStatic {
case StartsHumongous: return "Starts Humongous"; case StartsHumongous: return "Starts Humongous";
case ContinuesHumongous: return "Continues Humongous"; case ContinuesHumongous: return "Continues Humongous";
case Old: return "Old"; case Old: return "Old";
case Archive: return "Archive"; case Pinned: return "Pinned";
case OpenArchive: return "OpenArchive";
case ClosedArchive: return "ClosedArchive";
default: ShouldNotReachHere(); return NULL; default: ShouldNotReachHere(); return NULL;
} }
} }

View file

@ -234,30 +234,63 @@ public:
}; };
class VerifyArchiveOopClosure: public OopClosure { class VerifyArchiveOopClosure: public OopClosure {
HeapRegion* _hr;
public: public:
VerifyArchiveOopClosure(HeapRegion *hr) { } VerifyArchiveOopClosure(HeapRegion *hr)
: _hr(hr) { }
void do_oop(narrowOop *p) { do_oop_work(p); } void do_oop(narrowOop *p) { do_oop_work(p); }
void do_oop( oop *p) { do_oop_work(p); } void do_oop( oop *p) { do_oop_work(p); }
template <class T> void do_oop_work(T *p) { template <class T> void do_oop_work(T *p) {
oop obj = oopDesc::load_decode_heap_oop(p); oop obj = oopDesc::load_decode_heap_oop(p);
if (_hr->is_open_archive()) {
guarantee(obj == NULL || G1ArchiveAllocator::is_archive_object(obj), guarantee(obj == NULL || G1ArchiveAllocator::is_archive_object(obj),
"Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT, "Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT,
p2i(p), p2i(obj)); p2i(p), p2i(obj));
} else {
assert(_hr->is_closed_archive(), "should be closed archive region");
guarantee(obj == NULL || G1ArchiveAllocator::is_closed_archive_object(obj),
"Archive object at " PTR_FORMAT " references a non-archive object at " PTR_FORMAT,
p2i(p), p2i(obj));
}
} }
}; };
class VerifyArchiveRegionClosure: public ObjectClosure { class VerifyObjectInArchiveRegionClosure: public ObjectClosure {
HeapRegion* _hr;
public: public:
VerifyArchiveRegionClosure(HeapRegion *hr) { } VerifyObjectInArchiveRegionClosure(HeapRegion *hr, bool verbose)
: _hr(hr) { }
// Verify that all object pointers are to archive regions. // Verify that all object pointers are to archive regions.
void do_object(oop o) { void do_object(oop o) {
VerifyArchiveOopClosure checkOop(NULL); VerifyArchiveOopClosure checkOop(_hr);
assert(o != NULL, "Should not be here for NULL oops"); assert(o != NULL, "Should not be here for NULL oops");
o->oop_iterate_no_header(&checkOop); o->oop_iterate_no_header(&checkOop);
} }
}; };
// Should be only used at CDS dump time
class VerifyArchivePointerRegionClosure: public HeapRegionClosure {
private:
G1CollectedHeap* _g1h;
public:
VerifyArchivePointerRegionClosure(G1CollectedHeap* g1h) { }
virtual bool doHeapRegion(HeapRegion* r) {
if (r->is_archive()) {
VerifyObjectInArchiveRegionClosure verify_oop_pointers(r, false);
r->object_iterate(&verify_oop_pointers);
}
return false;
}
};
void G1HeapVerifier::verify_archive_regions() {
G1CollectedHeap* g1h = G1CollectedHeap::heap();
VerifyArchivePointerRegionClosure cl(NULL);
g1h->heap_region_iterate(&cl);
}
class VerifyRegionClosure: public HeapRegionClosure { class VerifyRegionClosure: public HeapRegionClosure {
private: private:
bool _par; bool _par;
@ -279,12 +312,15 @@ public:
bool doHeapRegion(HeapRegion* r) { bool doHeapRegion(HeapRegion* r) {
// For archive regions, verify there are no heap pointers to // For archive regions, verify there are no heap pointers to
// non-pinned regions. For all others, verify liveness info. // non-pinned regions. For all others, verify liveness info.
if (r->is_archive()) { if (r->is_closed_archive()) {
VerifyArchiveRegionClosure verify_oop_pointers(r); VerifyObjectInArchiveRegionClosure verify_oop_pointers(r, false);
r->object_iterate(&verify_oop_pointers); r->object_iterate(&verify_oop_pointers);
return true; return true;
} } else if (r->is_open_archive()) {
if (!r->is_continues_humongous()) { VerifyObjsInRegionClosure verify_open_archive_oop(r, _vo);
r->object_iterate(&verify_open_archive_oop);
return true;
} else if (!r->is_continues_humongous()) {
bool failures = false; bool failures = false;
r->verify(_vo, &failures); r->verify(_vo, &failures);
if (failures) { if (failures) {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -109,6 +109,8 @@ public:
void verify_not_dirty_region(HeapRegion* hr) PRODUCT_RETURN; void verify_not_dirty_region(HeapRegion* hr) PRODUCT_RETURN;
void verify_dirty_region(HeapRegion* hr) PRODUCT_RETURN; void verify_dirty_region(HeapRegion* hr) PRODUCT_RETURN;
void verify_dirty_young_regions() PRODUCT_RETURN; void verify_dirty_young_regions() PRODUCT_RETURN;
static void verify_archive_regions();
}; };
#endif // SHARE_VM_GC_G1_G1HEAPVERIFIER_HPP #endif // SHARE_VM_GC_G1_G1HEAPVERIFIER_HPP

View file

@ -222,7 +222,7 @@ class G1AdjustPointersClosure: public HeapRegionClosure {
// point all the oops to the new location // point all the oops to the new location
MarkSweep::adjust_pointers(obj); MarkSweep::adjust_pointers(obj);
} }
} else if (!r->is_pinned()) { } else if (!r->is_closed_archive()) {
// This really ought to be "as_CompactibleSpace"... // This really ought to be "as_CompactibleSpace"...
r->adjust_pointers(); r->adjust_pointers();
} }

View file

@ -185,14 +185,25 @@ void HeapRegion::set_survivor() {
_type.set_survivor(); _type.set_survivor();
} }
void HeapRegion::move_to_old() {
if (_type.relabel_as_old()) {
report_region_type_change(G1HeapRegionTraceType::Old);
}
}
void HeapRegion::set_old() { void HeapRegion::set_old() {
report_region_type_change(G1HeapRegionTraceType::Old); report_region_type_change(G1HeapRegionTraceType::Old);
_type.set_old(); _type.set_old();
} }
void HeapRegion::set_archive() { void HeapRegion::set_open_archive() {
report_region_type_change(G1HeapRegionTraceType::Archive); report_region_type_change(G1HeapRegionTraceType::OpenArchive);
_type.set_archive(); _type.set_open_archive();
}
void HeapRegion::set_closed_archive() {
report_region_type_change(G1HeapRegionTraceType::ClosedArchive);
_type.set_closed_archive();
} }
void HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) { void HeapRegion::set_starts_humongous(HeapWord* obj_top, size_t fill_size) {

View file

@ -434,6 +434,8 @@ class HeapRegion: public G1ContiguousSpace {
// should not be marked during mark/sweep. This allows the address // should not be marked during mark/sweep. This allows the address
// space to be shared by JVM instances. // space to be shared by JVM instances.
bool is_archive() const { return _type.is_archive(); } bool is_archive() const { return _type.is_archive(); }
bool is_open_archive() const { return _type.is_open_archive(); }
bool is_closed_archive() const { return _type.is_closed_archive(); }
// For a humongous region, region in which it starts. // For a humongous region, region in which it starts.
HeapRegion* humongous_start_region() const { HeapRegion* humongous_start_region() const {
@ -618,9 +620,11 @@ class HeapRegion: public G1ContiguousSpace {
void set_eden_pre_gc(); void set_eden_pre_gc();
void set_survivor(); void set_survivor();
void move_to_old();
void set_old(); void set_old();
void set_archive(); void set_open_archive();
void set_closed_archive();
// Determine if an object has been allocated since the last // Determine if an object has been allocated since the last
// mark performed by the collector. This returns true iff the object // mark performed by the collector. This returns true iff the object

View file

@ -117,7 +117,8 @@ inline bool HeapRegion::is_obj_dead_with_size(const oop obj, const G1CMBitMap* c
HeapWord* addr = (HeapWord*) obj; HeapWord* addr = (HeapWord*) obj;
assert(addr < top(), "must be"); assert(addr < top(), "must be");
assert(!is_archive(), "Archive regions should not have references into interesting regions."); assert(!is_closed_archive(),
"Closed archive regions should not have references into other regions");
assert(!is_humongous(), "Humongous objects not handled here"); assert(!is_humongous(), "Humongous objects not handled here");
bool obj_is_dead = is_obj_dead(obj, prev_bitmap); bool obj_is_dead = is_obj_dead(obj, prev_bitmap);
@ -162,7 +163,9 @@ inline size_t HeapRegion::block_size_using_bitmap(const HeapWord* addr, const G1
inline bool HeapRegion::is_obj_dead(const oop obj, const G1CMBitMap* const prev_bitmap) const { inline bool HeapRegion::is_obj_dead(const oop obj, const G1CMBitMap* const prev_bitmap) const {
assert(is_in_reserved(obj), "Object " PTR_FORMAT " must be in region", p2i(obj)); assert(is_in_reserved(obj), "Object " PTR_FORMAT " must be in region", p2i(obj));
return !obj_allocated_since_prev_marking(obj) && !prev_bitmap->is_marked((HeapWord*)obj); return !obj_allocated_since_prev_marking(obj) &&
!prev_bitmap->is_marked((HeapWord*)obj) &&
!is_open_archive();
} }
inline size_t HeapRegion::block_size(const HeapWord *addr) const { inline size_t HeapRegion::block_size(const HeapWord *addr) const {

View file

@ -34,7 +34,8 @@ bool HeapRegionType::is_valid(Tag tag) {
case StartsHumongousTag: case StartsHumongousTag:
case ContinuesHumongousTag: case ContinuesHumongousTag:
case OldTag: case OldTag:
case ArchiveTag: case OpenArchiveTag:
case ClosedArchiveTag:
return true; return true;
default: default:
return false; return false;
@ -50,7 +51,8 @@ const char* HeapRegionType::get_str() const {
case StartsHumongousTag: return "HUMS"; case StartsHumongousTag: return "HUMS";
case ContinuesHumongousTag: return "HUMC"; case ContinuesHumongousTag: return "HUMC";
case OldTag: return "OLD"; case OldTag: return "OLD";
case ArchiveTag: return "ARC"; case OpenArchiveTag: return "OARC";
case ClosedArchiveTag: return "CARC";
default: default:
ShouldNotReachHere(); ShouldNotReachHere();
return NULL; // keep some compilers happy return NULL; // keep some compilers happy
@ -66,7 +68,8 @@ const char* HeapRegionType::get_short_str() const {
case StartsHumongousTag: return "HS"; case StartsHumongousTag: return "HS";
case ContinuesHumongousTag: return "HC"; case ContinuesHumongousTag: return "HC";
case OldTag: return "O"; case OldTag: return "O";
case ArchiveTag: return "A"; case OpenArchiveTag: return "OA";
case ClosedArchiveTag: return "CA";
default: default:
ShouldNotReachHere(); ShouldNotReachHere();
return NULL; // keep some compilers happy return NULL; // keep some compilers happy
@ -82,7 +85,8 @@ G1HeapRegionTraceType::Type HeapRegionType::get_trace_type() {
case StartsHumongousTag: return G1HeapRegionTraceType::StartsHumongous; case StartsHumongousTag: return G1HeapRegionTraceType::StartsHumongous;
case ContinuesHumongousTag: return G1HeapRegionTraceType::ContinuesHumongous; case ContinuesHumongousTag: return G1HeapRegionTraceType::ContinuesHumongous;
case OldTag: return G1HeapRegionTraceType::Old; case OldTag: return G1HeapRegionTraceType::Old;
case ArchiveTag: return G1HeapRegionTraceType::Archive; case OpenArchiveTag: return G1HeapRegionTraceType::OpenArchive;
case ClosedArchiveTag: return G1HeapRegionTraceType::ClosedArchive;
default: default:
ShouldNotReachHere(); ShouldNotReachHere();
return G1HeapRegionTraceType::Free; // keep some compilers happy return G1HeapRegionTraceType::Free; // keep some compilers happy

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -43,20 +43,23 @@ private:
// future, we'll have to increase the size of the latter and hence // future, we'll have to increase the size of the latter and hence
// decrease the size of the former. // decrease the size of the former.
// //
// 0000 0 [ 0] Free // 00000 0 [ 0] Free
// //
// 0001 0 [ 2] Young Mask // 00001 0 [ 2] Young Mask
// 0001 0 [ 2] Eden // 00001 0 [ 2] Eden
// 0001 1 [ 3] Survivor // 00001 1 [ 3] Survivor
// //
// 0010 0 [ 4] Humongous Mask // 00010 0 [ 4] Humongous Mask
// 0100 0 [ 8] Pinned Mask // 00100 0 [ 8] Pinned Mask
// 0110 0 [12] Starts Humongous // 00110 0 [12] Starts Humongous
// 0110 1 [13] Continues Humongous // 00110 1 [13] Continues Humongous
// //
// 1000 0 [16] Old Mask // 01000 0 [16] Old Mask
//
// 10000 0 [32] Archive Mask
// 11100 0 [56] Open Archive
// 11100 1 [57] Closed Archive
// //
// 1100 0 [24] Archive
typedef enum { typedef enum {
FreeTag = 0, FreeTag = 0,
@ -72,7 +75,18 @@ private:
OldMask = 16, OldMask = 16,
OldTag = OldMask, OldTag = OldMask,
ArchiveTag = PinnedMask | OldMask // Archive regions are regions with immutable content (i.e. not reclaimed, and
// not allocated into during regular operation). They differ in the kind of references
// allowed for the contained objects:
// - Closed archive regions form a separate self-contained (closed) object graph
// within the set of all of these regions. No references outside of closed
// archive regions are allowed.
// - Open archive regions have no restrictions on the references of their objects.
// Objects within these regions are allowed to have references to objects
// contained in any other kind of regions.
ArchiveMask = 32,
OpenArchiveTag = ArchiveMask | PinnedMask | OldMask,
ClosedArchiveTag = ArchiveMask | PinnedMask | OldMask + 1
} Tag; } Tag;
volatile Tag _tag; volatile Tag _tag;
@ -115,7 +129,9 @@ public:
bool is_starts_humongous() const { return get() == StartsHumongousTag; } bool is_starts_humongous() const { return get() == StartsHumongousTag; }
bool is_continues_humongous() const { return get() == ContinuesHumongousTag; } bool is_continues_humongous() const { return get() == ContinuesHumongousTag; }
bool is_archive() const { return get() == ArchiveTag; } bool is_archive() const { return (get() & ArchiveMask) != 0; }
bool is_open_archive() const { return get() == OpenArchiveTag; }
bool is_closed_archive() const { return get() == ClosedArchiveTag; }
// is_old regions may or may not also be pinned // is_old regions may or may not also be pinned
bool is_old() const { return (get() & OldMask) != 0; } bool is_old() const { return (get() & OldMask) != 0; }
@ -138,7 +154,27 @@ public:
void set_old() { set(OldTag); } void set_old() { set(OldTag); }
void set_archive() { set_from(ArchiveTag, FreeTag); } // Change the current region type to be of an old region type if not already done so.
// Returns whether the region type has been changed or not.
bool relabel_as_old() {
//assert(!is_free(), "Should not try to move Free region");
assert(!is_humongous(), "Should not try to move Humongous region");
if (is_old()) {
return false;
}
if (is_eden()) {
set_from(OldTag, EdenTag);
return true;
} else if (is_free()) {
set_from(OldTag, FreeTag);
return true;
} else {
set_from(OldTag, SurvTag);
return true;
}
}
void set_open_archive() { set_from(OpenArchiveTag, FreeTag); }
void set_closed_archive() { set_from(ClosedArchiveTag, FreeTag); }
// Misc // Misc

View file

@ -87,7 +87,7 @@ template <class T> inline void MarkSweep::mark_and_push(T* p) {
if (!oopDesc::is_null(heap_oop)) { if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
if (!obj->mark()->is_marked() && if (!obj->mark()->is_marked() &&
!is_archive_object(obj)) { !is_closed_archive_object(obj)) {
mark_object(obj); mark_object(obj);
_marking_stack.push(obj); _marking_stack.push(obj);
} }
@ -184,7 +184,7 @@ template <class T> inline void MarkSweep::follow_root(T* p) {
if (!oopDesc::is_null(heap_oop)) { if (!oopDesc::is_null(heap_oop)) {
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
if (!obj->mark()->is_marked() && if (!obj->mark()->is_marked() &&
!is_archive_object(obj)) { !is_closed_archive_object(obj)) {
mark_object(obj); mark_object(obj);
follow_object(obj); follow_object(obj);
} }
@ -268,7 +268,7 @@ void MarkSweep::restore_marks() {
MarkSweep::IsAliveClosure MarkSweep::is_alive; MarkSweep::IsAliveClosure MarkSweep::is_alive;
bool MarkSweep::IsAliveClosure::do_object_b(oop p) { return p->is_gc_marked() || is_archive_object(p); } bool MarkSweep::IsAliveClosure::do_object_b(oop p) { return p->is_gc_marked() || is_closed_archive_object(p); }
MarkSweep::KeepAliveClosure MarkSweep::keep_alive; MarkSweep::KeepAliveClosure MarkSweep::keep_alive;

View file

@ -134,6 +134,8 @@ class MarkSweep : AllStatic {
static void set_ref_processor(ReferenceProcessor* rp); static void set_ref_processor(ReferenceProcessor* rp);
// Archive Object handling // Archive Object handling
static inline bool is_closed_archive_object(oop object);
static inline bool is_open_archive_object(oop object);
static inline bool is_archive_object(oop object); static inline bool is_archive_object(oop object);
static STWGCTimer* gc_timer() { return _gc_timer; } static STWGCTimer* gc_timer() { return _gc_timer; }

View file

@ -26,6 +26,7 @@
#define SHARE_VM_GC_SERIAL_MARKSWEEP_INLINE_HPP #define SHARE_VM_GC_SERIAL_MARKSWEEP_INLINE_HPP
#include "gc/serial/markSweep.hpp" #include "gc/serial/markSweep.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/universe.hpp" #include "memory/universe.hpp"
#include "oops/markOop.inline.hpp" #include "oops/markOop.inline.hpp"
#include "oops/oop.inline.hpp" #include "oops/oop.inline.hpp"
@ -33,6 +34,22 @@
#include "gc/g1/g1Allocator.inline.hpp" #include "gc/g1/g1Allocator.inline.hpp"
#endif // INCLUDE_ALL_GCS #endif // INCLUDE_ALL_GCS
inline bool MarkSweep::is_closed_archive_object(oop object) {
#if INCLUDE_ALL_GCS
return G1ArchiveAllocator::is_closed_archive_object(object);
#else
return false;
#endif
}
inline bool MarkSweep::is_open_archive_object(oop object) {
#if INCLUDE_ALL_GCS
return G1ArchiveAllocator::is_open_archive_object(object);
#else
return false;
#endif
}
inline bool MarkSweep::is_archive_object(oop object) { inline bool MarkSweep::is_archive_object(oop object) {
#if INCLUDE_ALL_GCS #if INCLUDE_ALL_GCS
return G1ArchiveAllocator::is_archive_object(object); return G1ArchiveAllocator::is_archive_object(object);
@ -52,14 +69,24 @@ template <class T> inline void MarkSweep::adjust_pointer(T* p) {
assert(Universe::heap()->is_in(obj), "should be in heap"); assert(Universe::heap()->is_in(obj), "should be in heap");
oop new_obj = oop(obj->mark()->decode_pointer()); oop new_obj = oop(obj->mark()->decode_pointer());
assert(is_archive_object(obj) || // no forwarding of archive objects assert(is_archive_object(obj) || // no forwarding of archive objects
new_obj != NULL || // is forwarding ptr? new_obj != NULL || // is forwarding ptr?
obj->mark() == markOopDesc::prototype() || // not gc marked? obj->mark() == markOopDesc::prototype() || // not gc marked?
(UseBiasedLocking && obj->mark()->has_bias_pattern()), (UseBiasedLocking && obj->mark()->has_bias_pattern()),
// not gc marked? // not gc marked?
"should be forwarded"); "should be forwarded");
#ifndef PRODUCT
// open_archive objects are marked by GC. Their mark should
// not have forwarding ptr.
if (is_open_archive_object(obj)) {
assert(new_obj == NULL, "archive heap object has forwarding ptr");
}
#endif
if (new_obj != NULL) { if (new_obj != NULL) {
if (!is_archive_object(obj)) { if (!is_closed_archive_object(obj)) {
assert(Universe::heap()->is_in_reserved(new_obj), assert(Universe::heap()->is_in_reserved(new_obj),
"should be in object space"); "should be in object space");
oopDesc::encode_store_heap_oop_not_null(p, new_obj); oopDesc::encode_store_heap_oop_not_null(p, new_obj);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -447,6 +447,9 @@ public:
// Return a size with adjustments as required of the space. // Return a size with adjustments as required of the space.
virtual size_t adjust_object_size_v(size_t size) const { return size; } virtual size_t adjust_object_size_v(size_t size) const { return size; }
void set_first_dead(HeapWord* value) { _first_dead = value; }
void set_end_of_live(HeapWord* value) { _end_of_live = value; }
protected: protected:
// Used during compaction. // Used during compaction.
HeapWord* _first_dead; HeapWord* _first_dead;

View file

@ -351,7 +351,8 @@ bool FileMapInfo::init_from_file(int fd) {
size_t len = lseek(fd, 0, SEEK_END); size_t len = lseek(fd, 0, SEEK_END);
struct FileMapInfo::FileMapHeader::space_info* si = struct FileMapInfo::FileMapHeader::space_info* si =
&_header->_space[MetaspaceShared::last_valid_region]; &_header->_space[MetaspaceShared::last_valid_region];
if (si->_file_offset >= len || len - si->_file_offset < si->_used) { // The last space might be empty
if (si->_file_offset > len || len - si->_file_offset < si->_used) {
fail_continue("The shared archive file has been truncated."); fail_continue("The shared archive file has been truncated.");
return false; return false;
} }
@ -443,7 +444,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
} else { } else {
si->_file_offset = _file_offset; si->_file_offset = _file_offset;
} }
if (MetaspaceShared::is_string_region(region)) { if (MetaspaceShared::is_heap_region(region)) {
assert((base - (char*)Universe::narrow_oop_base()) % HeapWordSize == 0, "Sanity"); assert((base - (char*)Universe::narrow_oop_base()) % HeapWordSize == 0, "Sanity");
if (base != NULL) { if (base != NULL) {
si->_addr._offset = (intx)oopDesc::encode_heap_oop_not_null((oop)base); si->_addr._offset = (intx)oopDesc::encode_heap_oop_not_null((oop)base);
@ -460,76 +461,66 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
write_bytes_aligned(base, (int)size); write_bytes_aligned(base, (int)size);
} }
// Write the string space. The string space contains one or multiple GC(G1) regions. // Write out the given archive heap memory regions. GC code combines multiple
// When the total string space size is smaller than one GC region of the dump time, // consecutive archive GC regions into one MemRegion whenever possible and
// only one string region is used for shared strings. // produces the 'heap_mem' array.
// //
// If the total string space size is bigger than one GC region, there would be more // If the archive heap memory size is smaller than a single dump time GC region
// than one GC regions allocated for shared strings. The first/bottom GC region might // size, there is only one MemRegion in the array.
// 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.
// //
// Here's the mapping from (GrowableArray<MemRegion> *regions) -> (metaspace string regions). // If the archive heap memory size is bigger than one dump time GC region size,
// + We have 1 or more heap regions: r0, r1, r2 ..... rn // the 'heap_mem' array may contain more than one consolidated MemRegions. When
// + We have 2 metaspace string regions: s0 and s1 // the first/bottom archive GC region is a partial GC region (with the empty
// portion at the higher address within the region), one MemRegion is used for
// the bottom partial archive GC region. The rest of the consecutive archive
// GC regions are combined into another MemRegion.
// //
// If there's a single heap region (r0), then s0 == r0, and s1 is empty. // Here's the mapping from (archive heap GC regions) -> (GrowableArray<MemRegion> *regions).
// + We have 1 or more archive heap regions: ah0, ah1, ah2 ..... ahn
// + We have 1 or 2 consolidated heap memory regions: r0 and r1
//
// If there's a single archive GC region (ah0), then r0 == ah0, and r1 is empty.
// Otherwise: // Otherwise:
// //
// "X" represented space that's occupied by heap objects. // "X" represented space that's occupied by heap objects.
// "_" represented unused spaced in the heap region. // "_" represented unused spaced in the heap region.
// //
// //
// |r0 | r1 | r2 | ...... | rn | // |ah0 | ah1 | ah2| ...... | ahn |
// |XXXXXX|__ |XXXXX|XXXX|XXXXXXXX|XXXX| // |XXXXXX|__ |XXXXX|XXXX|XXXXXXXX|XXXX|
// |<-s0->| |<- s1 ----------------->| // |<-r0->| |<- r1 ----------------->|
// ^^^ // ^^^
// | // |
// +-- unmapped space // +-- gap
void FileMapInfo::write_string_regions(GrowableArray<MemRegion> *regions, size_t FileMapInfo::write_archive_heap_regions(GrowableArray<MemRegion> *heap_mem,
char** st0_start, char** st0_top, char** st0_end, int first_region_id, int max_num_regions) {
char** st1_start, char** st1_top, char** st1_end) { assert(max_num_regions <= 2, "Only support maximum 2 memory regions");
*st0_start = *st0_top = *st0_end = NULL;
*st1_start = *st1_top = *st1_end = NULL;
assert(MetaspaceShared::max_strings == 2, "this loop doesn't work for any other value"); int arr_len = heap_mem == NULL ? 0 : heap_mem->length();
for (int i = MetaspaceShared::first_string; if(arr_len > max_num_regions) {
i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { fail_stop("Unable to write archive heap memory regions: "
"number of memory regions exceeds maximum due to fragmentation");
}
size_t total_size = 0;
for (int i = first_region_id, arr_idx = 0;
i < first_region_id + max_num_regions;
i++, arr_idx++) {
char* start = NULL; char* start = NULL;
size_t size = 0; size_t size = 0;
int len = regions->length(); if (arr_idx < arr_len) {
if (len > 0) { start = (char*)heap_mem->at(arr_idx).start();
if (i == MetaspaceShared::first_string) { size = heap_mem->at(arr_idx).byte_size();
MemRegion first = regions->first(); total_size += size;
start = (char*)first.start();
size = first.byte_size();
*st0_start = start;
*st0_top = start + size;
if (len > 1) {
*st0_end = (char*)regions->at(1).start();
} else {
*st0_end = start + size;
} }
} else {
assert(i == MetaspaceShared::first_string + 1, "must be"); log_info(cds)("Archive heap region %d " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes",
if (len > 1) {
start = (char*)regions->at(1).start();
size = (char*)regions->at(len - 1).end() - start;
*st1_start = start;
*st1_top = start + size;
*st1_end = start + size;
}
}
}
log_info(cds)("String region %d " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes",
i, p2i(start), p2i(start + size), size); i, p2i(start), p2i(start + size), size);
write_region(i, start, size, false, false); write_region(i, start, size, false, false);
} }
return total_size;
} }
// Dump bytes to file -- at the current file position. // Dump bytes to file -- at the current file position.
void FileMapInfo::write_bytes(const void* buffer, int nbytes) { void FileMapInfo::write_bytes(const void* buffer, int nbytes) {
@ -641,11 +632,11 @@ ReservedSpace FileMapInfo::reserve_shared_memory() {
} }
// Memory map a region in the address space. // Memory map a region in the address space.
static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode", static const char* shared_region_name[] = { "MiscData", "ReadWrite", "ReadOnly", "MiscCode", "OptionalData",
"String1", "String2", "OptionalData" }; "String1", "String2", "OpenArchive1", "OpenArchive2" };
char* FileMapInfo::map_region(int i) { char* FileMapInfo::map_region(int i) {
assert(!MetaspaceShared::is_string_region(i), "sanity"); assert(!MetaspaceShared::is_heap_region(i), "sanity");
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
size_t used = si->_used; size_t used = si->_used;
size_t alignment = os::vm_allocation_granularity(); size_t alignment = os::vm_allocation_granularity();
@ -675,101 +666,144 @@ char* FileMapInfo::map_region(int i) {
} }
static MemRegion *string_ranges = NULL; static MemRegion *string_ranges = NULL;
static int num_ranges = 0; static MemRegion *open_archive_heap_ranges = NULL;
bool FileMapInfo::map_string_regions() { static int num_string_ranges = 0;
#if INCLUDE_ALL_GCS static int num_open_archive_heap_ranges = 0;
if (UseG1GC && UseCompressedOops && UseCompressedClassPointers) {
#if INCLUDE_CDS_JAVA_HEAP
//
// Map the shared string objects and open archive heap objects to the runtime
// java heap.
//
// The shared strings are mapped near the runtime java heap top. The
// mapped strings contain no out-going references to any other java heap
// regions. GC does not write into the mapped shared strings.
//
// The open archive heap objects are mapped below the shared strings in
// the runtime java heap. The mapped open archive heap data only contain
// references to the shared strings and open archive objects initially.
// During runtime execution, out-going references to any other java heap
// regions may be added. GC may mark and update references in the mapped
// open archive objects.
void FileMapInfo::map_heap_regions() {
if (MetaspaceShared::is_heap_object_archiving_allowed()) {
// Check that all the narrow oop and klass encodings match the archive // Check that all the narrow oop and klass encodings match the archive
if (narrow_oop_mode() != Universe::narrow_oop_mode() || if (narrow_oop_mode() != Universe::narrow_oop_mode() ||
narrow_oop_shift() != Universe::narrow_oop_shift() || narrow_oop_shift() != Universe::narrow_oop_shift() ||
narrow_klass_base() != Universe::narrow_klass_base() || narrow_klass_base() != Universe::narrow_klass_base() ||
narrow_klass_shift() != Universe::narrow_klass_shift()) { narrow_klass_shift() != Universe::narrow_klass_shift()) {
if (log_is_enabled(Info, cds) && _header->_space[MetaspaceShared::first_string]._used > 0) { if (log_is_enabled(Info, cds) && _header->_space[MetaspaceShared::first_string]._used > 0) {
log_info(cds)("Shared string data from the CDS archive is being ignored. " log_info(cds)("Cached heap data from the CDS archive is being ignored. "
"The current CompressedOops/CompressedClassPointers encoding differs from " "The current CompressedOops/CompressedClassPointers encoding differs from "
"that archived due to heap size change. The archive was dumped using max heap " "that archived due to heap size change. The archive was dumped using max heap "
"size " UINTX_FORMAT "M.", max_heap_size()/M); "size " UINTX_FORMAT "M.", max_heap_size()/M);
} }
} else { } else {
string_ranges = new MemRegion[MetaspaceShared::max_strings]; // First, map string regions as closed archive heap regions.
struct FileMapInfo::FileMapHeader::space_info* si; // GC does not write into the regions.
if (map_heap_data(&string_ranges,
MetaspaceShared::first_string,
MetaspaceShared::max_strings,
&num_string_ranges)) {
StringTable::set_shared_string_mapped();
for (int i = MetaspaceShared::first_string; // Now, map open_archive heap regions, GC can write into the regions.
i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { if (map_heap_data(&open_archive_heap_ranges,
MetaspaceShared::first_open_archive_heap_region,
MetaspaceShared::max_open_archive_heap_region,
&num_open_archive_heap_ranges,
true /* open */)) {
MetaspaceShared::set_open_archive_heap_region_mapped();
}
}
}
} else {
if (log_is_enabled(Info, cds) && _header->_space[MetaspaceShared::first_string]._used > 0) {
log_info(cds)("Cached heap data from the CDS archive is being ignored. UseG1GC, "
"UseCompressedOops and UseCompressedClassPointers are required.");
}
}
if (!StringTable::shared_string_mapped()) {
assert(string_ranges == NULL && num_string_ranges == 0, "sanity");
}
if (!MetaspaceShared::open_archive_heap_region_mapped()) {
assert(open_archive_heap_ranges == NULL && num_open_archive_heap_ranges == 0, "sanity");
}
}
bool FileMapInfo::map_heap_data(MemRegion **heap_mem, int first,
int max, int* num, bool is_open_archive) {
MemRegion * regions = new MemRegion[max];
struct FileMapInfo::FileMapHeader::space_info* si;
int region_num = 0;
for (int i = first;
i < first + max; i++) {
si = &_header->_space[i]; si = &_header->_space[i];
size_t used = si->_used; size_t used = si->_used;
if (used > 0) { if (used > 0) {
size_t size = used; size_t size = used;
char* requested_addr = (char*)((void*)oopDesc::decode_heap_oop_not_null( char* requested_addr = (char*)((void*)oopDesc::decode_heap_oop_not_null(
(narrowOop)si->_addr._offset)); (narrowOop)si->_addr._offset));
string_ranges[num_ranges] = MemRegion((HeapWord*)requested_addr, size / HeapWordSize); regions[region_num] = MemRegion((HeapWord*)requested_addr, size / HeapWordSize);
num_ranges ++; region_num ++;
} }
} }
if (num_ranges == 0) { if (region_num == 0) {
StringTable::ignore_shared_strings(true); return false; // no archived java heap data
return true; // no shared string data
} }
// Check that ranges are within the java heap // Check that ranges are within the java heap
if (!G1CollectedHeap::heap()->check_archive_addresses(string_ranges, num_ranges)) { if (!G1CollectedHeap::heap()->check_archive_addresses(regions, region_num)) {
fail_continue("Unable to allocate shared string space: range is not " log_info(cds)("UseSharedSpaces: Unable to allocate region, "
"within java heap."); "range is not within java heap.");
return false; return false;
} }
// allocate from java heap // allocate from java heap
if (!G1CollectedHeap::heap()->alloc_archive_regions(string_ranges, num_ranges)) { if (!G1CollectedHeap::heap()->alloc_archive_regions(
fail_continue("Unable to allocate shared string space: range is " regions, region_num, is_open_archive)) {
"already in use."); log_info(cds)("UseSharedSpaces: Unable to allocate region, "
"java heap range is already in use.");
return false; return false;
} }
// Map the string data. No need to call MemTracker::record_virtual_memory_type() // Map the archived heap data. No need to call MemTracker::record_virtual_memory_type()
// for mapped string regions as they are part of the reserved java heap, which // for mapped regions as they are part of the reserved java heap, which is
// is already recorded. // already recorded.
for (int i = 0; i < num_ranges; i++) { for (int i = 0; i < region_num; i++) {
si = &_header->_space[MetaspaceShared::first_string + i]; si = &_header->_space[first + i];
char* addr = (char*)string_ranges[i].start(); char* addr = (char*)regions[i].start();
char* base = os::map_memory(_fd, _full_path, si->_file_offset, char* base = os::map_memory(_fd, _full_path, si->_file_offset,
addr, string_ranges[i].byte_size(), si->_read_only, addr, regions[i].byte_size(), si->_read_only,
si->_allow_exec); si->_allow_exec);
if (base == NULL || base != addr) { if (base == NULL || base != addr) {
// dealloc the string regions from java heap // dealloc the regions from java heap
dealloc_string_regions(); dealloc_archive_heap_regions(regions, region_num);
fail_continue("Unable to map shared string space at required address."); log_info(cds)("UseSharedSpaces: Unable to map at required address in java heap.");
return false; return false;
} }
} }
if (!verify_string_regions()) { if (!verify_mapped_heap_regions(first, region_num)) {
// dealloc the string regions from java heap // dealloc the regions from java heap
dealloc_string_regions(); dealloc_archive_heap_regions(regions, region_num);
fail_continue("Shared string regions are corrupt"); log_info(cds)("UseSharedSpaces: mapped heap regions are corrupt");
return false; return false;
} }
// the shared string data is mapped successfully // the shared heap data is mapped successfully
return true; *heap_mem = regions;
} *num = region_num;
} else {
if (log_is_enabled(Info, cds) && _header->_space[MetaspaceShared::first_string]._used > 0) {
log_info(cds)("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; return true;
} }
bool FileMapInfo::verify_string_regions() { bool FileMapInfo::verify_mapped_heap_regions(int first, int num) {
for (int i = MetaspaceShared::first_string; for (int i = first;
i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) { i <= first + num; i++) {
if (!verify_region_checksum(i)) { if (!verify_region_checksum(i)) {
return false; return false;
} }
@ -777,17 +811,31 @@ bool FileMapInfo::verify_string_regions() {
return true; return true;
} }
void FileMapInfo::fixup_string_regions() { void FileMapInfo::fixup_mapped_heap_regions() {
#if INCLUDE_ALL_GCS
// If any string regions were found, call the fill routine to make them parseable. // If any string regions were found, call the fill routine to make them parseable.
// Note that string_ranges may be non-NULL even if no ranges were found. // Note that string_ranges may be non-NULL even if no ranges were found.
if (num_ranges != 0) { if (num_string_ranges != 0) {
assert(string_ranges != NULL, "Null string_ranges array with non-zero count"); assert(string_ranges != NULL, "Null string_ranges array with non-zero count");
G1CollectedHeap::heap()->fill_archive_regions(string_ranges, num_ranges); G1CollectedHeap::heap()->fill_archive_regions(string_ranges, num_string_ranges);
}
// do the same for mapped open archive heap regions
if (num_open_archive_heap_ranges != 0) {
assert(open_archive_heap_ranges != NULL, "NULL open_archive_heap_ranges array with non-zero count");
G1CollectedHeap::heap()->fill_archive_regions(open_archive_heap_ranges,
num_open_archive_heap_ranges);
} }
#endif
} }
// dealloc the archive regions from java heap
void FileMapInfo::dealloc_archive_heap_regions(MemRegion* regions, int num) {
if (num > 0) {
assert(regions != NULL, "Null archive ranges array with non-zero count");
G1CollectedHeap::heap()->dealloc_archive_regions(regions, num);
}
}
#endif // INCLUDE_CDS_JAVA_HEAP
bool FileMapInfo::verify_region_checksum(int i) { bool FileMapInfo::verify_region_checksum(int i) {
if (!VerifySharedSpaces) { if (!VerifySharedSpaces) {
return true; return true;
@ -798,8 +846,11 @@ bool FileMapInfo::verify_region_checksum(int i) {
if (sz == 0) { if (sz == 0) {
return true; // no data return true; // no data
} }
if (MetaspaceShared::is_string_region(i) && StringTable::shared_string_ignored()) { if ((MetaspaceShared::is_string_region(i) &&
return true; // shared string data are not mapped !StringTable::shared_string_mapped()) ||
(MetaspaceShared::is_open_archive_heap_region(i) &&
!MetaspaceShared::open_archive_heap_region_mapped())) {
return true; // archived heap data is not mapped
} }
const char* buf = _header->region_addr(i); const char* buf = _header->region_addr(i);
int crc = ClassLoader::crc32(0, buf, (jint)sz); int crc = ClassLoader::crc32(0, buf, (jint)sz);
@ -813,7 +864,7 @@ bool FileMapInfo::verify_region_checksum(int i) {
// Unmap a memory region in the address space. // Unmap a memory region in the address space.
void FileMapInfo::unmap_region(int i) { void FileMapInfo::unmap_region(int i) {
assert(!MetaspaceShared::is_string_region(i), "sanity"); assert(!MetaspaceShared::is_heap_region(i), "sanity");
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i]; struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
size_t used = si->_used; size_t used = si->_used;
size_t size = align_up(used, os::vm_allocation_granularity()); size_t size = align_up(used, os::vm_allocation_granularity());
@ -828,16 +879,6 @@ void FileMapInfo::unmap_region(int i) {
} }
} }
// dealloc the archived string region from java heap
void FileMapInfo::dealloc_string_regions() {
#if INCLUDE_ALL_GCS
if (num_ranges > 0) {
assert(string_ranges != NULL, "Null string_ranges array with non-zero count");
G1CollectedHeap::heap()->dealloc_archive_regions(string_ranges, num_ranges);
}
#endif
}
void FileMapInfo::assert_mark(bool check) { void FileMapInfo::assert_mark(bool check) {
if (!check) { if (!check) {
fail_stop("Mark mismatch while restoring from shared file."); fail_stop("Mark mismatch while restoring from shared file.");
@ -883,9 +924,9 @@ bool FileMapInfo::initialize() {
} }
char* FileMapInfo::FileMapHeader::region_addr(int idx) { char* FileMapInfo::FileMapHeader::region_addr(int idx) {
if (MetaspaceShared::is_string_region(idx)) { if (MetaspaceShared::is_heap_region(idx)) {
return (char*)((void*)oopDesc::decode_heap_oop_not_null( return _space[idx]._used > 0 ?
(narrowOop)_space[idx]._addr._offset)); (char*)((void*)oopDesc::decode_heap_oop_not_null((narrowOop)_space[idx]._addr._offset)) : NULL;
} else { } else {
return _space[idx]._addr._base; return _space[idx]._addr._base;
} }
@ -965,15 +1006,15 @@ bool FileMapInfo::validate_header() {
} }
// The following method is provided to see whether a given pointer // The following method is provided to see whether a given pointer
// falls in the mapped shared space. // falls in the mapped shared metadata space.
// Param: // Param:
// p, The given pointer // p, The given pointer
// Return: // Return:
// True if the p is within the mapped shared space, otherwise, false. // True if the p is within the mapped shared space, otherwise, false.
bool FileMapInfo::is_in_shared_space(const void* p) { bool FileMapInfo::is_in_shared_space(const void* p) {
for (int i = 0; i < MetaspaceShared::n_regions; i++) { for (int i = 0; i < MetaspaceShared::num_non_heap_spaces; i++) {
char *base; char *base;
if (MetaspaceShared::is_string_region(i) && _header->_space[i]._used == 0) { if (_header->_space[i]._used == 0) {
continue; continue;
} }
base = _header->region_addr(i); base = _header->region_addr(i);
@ -985,7 +1026,7 @@ bool FileMapInfo::is_in_shared_space(const void* p) {
return false; return false;
} }
// Check if a given address is within one of the shared regions ( ro, rw, mc or md) // Check if a given address is within one of the shared regions
bool FileMapInfo::is_in_shared_region(const void* p, int idx) { bool FileMapInfo::is_in_shared_region(const void* p, int idx) {
assert(idx == MetaspaceShared::ro || assert(idx == MetaspaceShared::ro ||
idx == MetaspaceShared::rw || idx == MetaspaceShared::rw ||
@ -1014,16 +1055,18 @@ void FileMapInfo::stop_sharing_and_unmap(const char* msg) {
FileMapInfo *map_info = FileMapInfo::current_info(); FileMapInfo *map_info = FileMapInfo::current_info();
if (map_info) { if (map_info) {
map_info->fail_continue("%s", msg); map_info->fail_continue("%s", msg);
for (int i = 0; i < MetaspaceShared::num_non_strings; i++) { for (int i = 0; i < MetaspaceShared::num_non_heap_spaces; i++) {
char *addr = map_info->_header->region_addr(i); char *addr = map_info->_header->region_addr(i);
if (addr != NULL && !MetaspaceShared::is_string_region(i)) { if (addr != NULL && !MetaspaceShared::is_heap_region(i)) {
map_info->unmap_region(i); map_info->unmap_region(i);
map_info->_header->_space[i]._addr._base = NULL; map_info->_header->_space[i]._addr._base = NULL;
} }
} }
// Dealloc the string regions only without unmapping. The string regions are part // Dealloc the archive heap regions only without unmapping. The regions are part
// of the java heap. Unmapping of the heap regions are managed by GC. // of the java heap. Unmapping of the heap regions are managed by GC.
map_info->dealloc_string_regions(); map_info->dealloc_archive_heap_regions(open_archive_heap_ranges,
num_open_archive_heap_ranges);
map_info->dealloc_archive_heap_regions(string_ranges, num_string_ranges);
} else if (DumpSharedSpaces) { } else if (DumpSharedSpaces) {
fail_stop("%s", msg); fail_stop("%s", msg);
} }

View file

@ -242,17 +242,14 @@ public:
void write_header(); void write_header();
void write_region(int region, char* base, size_t size, void write_region(int region, char* base, size_t size,
bool read_only, bool allow_exec); bool read_only, bool allow_exec);
void write_string_regions(GrowableArray<MemRegion> *regions, size_t write_archive_heap_regions(GrowableArray<MemRegion> *heap_mem,
char** s0_start, char** s0_top, char** s0_end, int first_region_id, int max_num_regions);
char** s1_start, char** s1_top, char** s1_end);
void write_bytes(const void* buffer, int count); void write_bytes(const void* buffer, int count);
void write_bytes_aligned(const void* buffer, int count); void write_bytes_aligned(const void* buffer, int count);
char* map_region(int i); char* map_region(int i);
bool map_string_regions(); void map_heap_regions() NOT_CDS_JAVA_HEAP_RETURN;
bool verify_string_regions(); void fixup_mapped_heap_regions() NOT_CDS_JAVA_HEAP_RETURN;
void fixup_string_regions();
void unmap_region(int i); void unmap_region(int i);
void dealloc_string_regions();
bool verify_region_checksum(int i); bool verify_region_checksum(int i);
void close(); void close();
bool is_open() { return _file_open; } bool is_open() { return _file_open; }
@ -294,6 +291,12 @@ public:
static int get_number_of_share_classpaths() { static int get_number_of_share_classpaths() {
return _classpath_entry_table_size; return _classpath_entry_table_size;
} }
private:
bool map_heap_data(MemRegion **heap_mem, int first, int max, int* num,
bool is_open = false) NOT_CDS_JAVA_HEAP_RETURN_(false);
bool verify_mapped_heap_regions(int first, int num) NOT_CDS_JAVA_HEAP_RETURN_(false);
void dealloc_archive_heap_regions(MemRegion* regions, int num) NOT_CDS_JAVA_HEAP_RETURN;
}; };
#endif // SHARE_VM_MEMORY_FILEMAP_HPP #endif // SHARE_VM_MEMORY_FILEMAP_HPP

View file

@ -3347,9 +3347,9 @@ void Metaspace::global_initialize() {
// If UseCompressedClassPointers is set then allocate the metaspace area // If UseCompressedClassPointers is set then allocate the metaspace area
// above the heap and above the CDS area (if it exists). // above the heap and above the CDS area (if it exists).
allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address); allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address);
// Map the shared string space after compressed pointers // map_heap_regions() compares the current narrow oop and klass encodings
// because it relies on compressed class pointers setting to work // with the archived ones, so it must be done after all encodings are determined.
mapinfo->map_string_regions(); mapinfo->map_heap_regions();
} }
#endif // _LP64 #endif // _LP64
} else { } else {

View file

@ -34,6 +34,11 @@
#include "classfile/systemDictionary.hpp" #include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp" #include "classfile/systemDictionaryShared.hpp"
#include "code/codeCache.hpp" #include "code/codeCache.hpp"
#if INCLUDE_ALL_GCS
#include "gc/g1/g1Allocator.inline.hpp"
#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1SATBCardTableModRefBS.hpp"
#endif
#include "gc/shared/gcLocker.hpp" #include "gc/shared/gcLocker.hpp"
#include "interpreter/bytecodeStream.hpp" #include "interpreter/bytecodeStream.hpp"
#include "interpreter/bytecodes.hpp" #include "interpreter/bytecodes.hpp"
@ -68,6 +73,7 @@ MetaspaceSharedStats MetaspaceShared::_stats;
bool MetaspaceShared::_has_error_classes; bool MetaspaceShared::_has_error_classes;
bool MetaspaceShared::_archive_loading_failed = false; bool MetaspaceShared::_archive_loading_failed = false;
bool MetaspaceShared::_remapped_readwrite = false; bool MetaspaceShared::_remapped_readwrite = false;
bool MetaspaceShared::_open_archive_heap_region_mapped = false;
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL; address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0; size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
size_t MetaspaceShared::_core_spaces_size = 0; size_t MetaspaceShared::_core_spaces_size = 0;
@ -79,10 +85,12 @@ size_t MetaspaceShared::_core_spaces_size = 0;
// md - misc data (the c++ vtables) // md - misc data (the c++ vtables)
// od - optional data (original class files) // od - optional data (original class files)
// //
// s0 - shared strings #0 // s0 - shared strings(closed archive heap space) #0
// s1 - shared strings #1 (may be empty) // s1 - shared strings(closed archive heap space) #1 (may be empty)
// oa0 - open archive heap space #0
// oa1 - open archive heap space #1 (may be empty)
// //
// Except for the s0/s1 regions, the other 5 regions are linearly allocated, starting from // The mc, rw, ro, md and od regions are linearly allocated, starting from
// SharedBaseAddress, in the order of mc->rw->ro->md->od. The size of these 5 regions // SharedBaseAddress, in the order of mc->rw->ro->md->od. The size of these 5 regions
// are page-aligned, and there's no gap between any consecutive regions. // are page-aligned, and there's no gap between any consecutive regions.
// //
@ -97,8 +105,8 @@ size_t MetaspaceShared::_core_spaces_size = 0;
// [5] C++ vtables are copied into the md region. // [5] C++ vtables are copied into the md region.
// [6] Original class files are copied into the od region. // [6] Original class files are copied into the od region.
// //
// The s0/s1 regions are populated inside MetaspaceShared::dump_string_and_symbols. Their // The s0/s1 and oa0/oa1 regions are populated inside MetaspaceShared::dump_java_heap_objects.
// layout is independent of the other 5 regions. // Their layout is independent of the other 5 regions.
class DumpRegion { class DumpRegion {
private: private:
@ -194,8 +202,9 @@ public:
} }
}; };
DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md"), _od_region("od"); DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md"), _od_region("od");
DumpRegion _s0_region("s0"), _s1_region("s1"); size_t _total_string_region_size = 0, _total_open_archive_region_size = 0;
char* MetaspaceShared::misc_code_space_alloc(size_t num_bytes) { char* MetaspaceShared::misc_code_space_alloc(size_t num_bytes) {
return _mc_region.allocate(num_bytes); return _mc_region.allocate(num_bytes);
@ -856,10 +865,14 @@ void DumpAllocStats::print_stats(int ro_all, int rw_all, int mc_all, int md_all)
class VM_PopulateDumpSharedSpace: public VM_Operation { class VM_PopulateDumpSharedSpace: public VM_Operation {
private: private:
GrowableArray<MemRegion> *_string_regions; GrowableArray<MemRegion> *_string_regions;
GrowableArray<MemRegion> *_open_archive_heap_regions;
void dump_string_and_symbols(); void dump_java_heap_objects() NOT_CDS_JAVA_HEAP_RETURN;
void dump_symbols();
char* dump_read_only_tables(); char* dump_read_only_tables();
void print_region_stats(); void print_region_stats();
void print_heap_region_stats(GrowableArray<MemRegion> *heap_mem,
const char *name, const size_t total_size);
public: public:
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
@ -1072,9 +1085,8 @@ public:
} }
// We must relocate the System::_well_known_klasses only after we have copied the // We must relocate the System::_well_known_klasses only after we have copied the
// strings in during dump_string_and_symbols(): during the string copy, we operate on old // java objects in during dump_java_heap_objects(): during the object copy, we operate on
// String objects which assert that their klass is the old // old objects which assert that their klass is the original klass.
// SystemDictionary::String_klass().
static void relocate_well_known_klasses() { static void relocate_well_known_klasses() {
{ {
tty->print_cr("Relocating SystemDictionary::_well_known_klasses[] ... "); tty->print_cr("Relocating SystemDictionary::_well_known_klasses[] ... ");
@ -1127,16 +1139,11 @@ void VM_PopulateDumpSharedSpace::write_region(FileMapInfo* mapinfo, int region_i
mapinfo->write_region(region_idx, dump_region->base(), dump_region->used(), read_only, allow_exec); mapinfo->write_region(region_idx, dump_region->base(), dump_region->used(), read_only, allow_exec);
} }
void VM_PopulateDumpSharedSpace::dump_string_and_symbols() { void VM_PopulateDumpSharedSpace::dump_symbols() {
tty->print_cr("Dumping string and symbol tables ..."); tty->print_cr("Dumping symbol table ...");
NOT_PRODUCT(SymbolTable::verify()); NOT_PRODUCT(SymbolTable::verify());
NOT_PRODUCT(StringTable::verify());
SymbolTable::write_to_archive(); SymbolTable::write_to_archive();
// The string space has maximum two regions. See FileMapInfo::write_string_regions() for details.
_string_regions = new GrowableArray<MemRegion>(2);
StringTable::write_to_archive(_string_regions);
} }
char* VM_PopulateDumpSharedSpace::dump_read_only_tables() { char* VM_PopulateDumpSharedSpace::dump_read_only_tables() {
@ -1206,7 +1213,6 @@ void VM_PopulateDumpSharedSpace::doit() {
tty->print_cr(" type array classes = %5d", num_type_array); tty->print_cr(" type array classes = %5d", num_type_array);
} }
// Ensure the ConstMethods won't be modified at run-time // Ensure the ConstMethods won't be modified at run-time
tty->print("Updating ConstMethods ... "); tty->print("Updating ConstMethods ... ");
rewrite_nofast_bytecodes_and_calculate_fingerprints(); rewrite_nofast_bytecodes_and_calculate_fingerprints();
@ -1220,7 +1226,13 @@ void VM_PopulateDumpSharedSpace::doit() {
ArchiveCompactor::initialize(); ArchiveCompactor::initialize();
ArchiveCompactor::copy_and_compact(); ArchiveCompactor::copy_and_compact();
dump_string_and_symbols(); dump_symbols();
// Dump supported java heap objects
_string_regions = NULL;
_open_archive_heap_regions = NULL;
dump_java_heap_objects();
ArchiveCompactor::relocate_well_known_klasses(); ArchiveCompactor::relocate_well_known_klasses();
char* read_only_tables_start = dump_read_only_tables(); char* read_only_tables_start = dump_read_only_tables();
@ -1258,9 +1270,6 @@ void VM_PopulateDumpSharedSpace::doit() {
mapinfo->set_cds_i2i_entry_code_buffers_size(MetaspaceShared::cds_i2i_entry_code_buffers_size()); mapinfo->set_cds_i2i_entry_code_buffers_size(MetaspaceShared::cds_i2i_entry_code_buffers_size());
mapinfo->set_core_spaces_size(core_spaces_size); mapinfo->set_core_spaces_size(core_spaces_size);
char* s0_start, *s0_top, *s0_end;
char* s1_start, *s1_top, *s1_end;
for (int pass=1; pass<=2; pass++) { for (int pass=1; pass<=2; pass++) {
if (pass == 1) { if (pass == 1) {
// The first pass doesn't actually write the data to disk. All it // The first pass doesn't actually write the data to disk. All it
@ -1282,9 +1291,14 @@ void VM_PopulateDumpSharedSpace::doit() {
write_region(mapinfo, MetaspaceShared::md, &_md_region, /*read_only=*/false,/*allow_exec=*/false); write_region(mapinfo, MetaspaceShared::md, &_md_region, /*read_only=*/false,/*allow_exec=*/false);
write_region(mapinfo, MetaspaceShared::od, &_od_region, /*read_only=*/true, /*allow_exec=*/false); write_region(mapinfo, MetaspaceShared::od, &_od_region, /*read_only=*/true, /*allow_exec=*/false);
mapinfo->write_string_regions(_string_regions, _total_string_region_size = mapinfo->write_archive_heap_regions(
&s0_start, &s0_top, &s0_end, _string_regions,
&s1_start, &s1_top, &s1_end); MetaspaceShared::first_string,
MetaspaceShared::max_strings);
_total_open_archive_region_size = mapinfo->write_archive_heap_regions(
_open_archive_heap_regions,
MetaspaceShared::first_open_archive_heap_region,
MetaspaceShared::max_open_archive_heap_region);
} }
mapinfo->close(); mapinfo->close();
@ -1292,8 +1306,6 @@ void VM_PopulateDumpSharedSpace::doit() {
// Restore the vtable in case we invoke any virtual methods. // Restore the vtable in case we invoke any virtual methods.
MetaspaceShared::clone_cpp_vtables((intptr_t*)vtbl_list); MetaspaceShared::clone_cpp_vtables((intptr_t*)vtbl_list);
_s0_region.init(s0_start, s0_top, s0_end);
_s1_region.init(s1_start, s1_top, s1_end);
print_region_stats(); print_region_stats();
if (log_is_enabled(Info, cds)) { if (log_is_enabled(Info, cds)) {
@ -1307,11 +1319,13 @@ void VM_PopulateDumpSharedSpace::print_region_stats() {
const size_t total_reserved = _ro_region.reserved() + _rw_region.reserved() + const size_t total_reserved = _ro_region.reserved() + _rw_region.reserved() +
_mc_region.reserved() + _md_region.reserved() + _mc_region.reserved() + _md_region.reserved() +
_od_region.reserved() + _od_region.reserved() +
_s0_region.reserved() + _s1_region.reserved(); _total_string_region_size +
_total_open_archive_region_size;
const size_t total_bytes = _ro_region.used() + _rw_region.used() + const size_t total_bytes = _ro_region.used() + _rw_region.used() +
_mc_region.used() + _md_region.used() + _mc_region.used() + _md_region.used() +
_od_region.used() + _od_region.used() +
_s0_region.used() + _s1_region.used(); _total_string_region_size +
_total_open_archive_region_size;
const double total_u_perc = total_bytes / double(total_reserved) * 100.0; const double total_u_perc = total_bytes / double(total_reserved) * 100.0;
_mc_region.print(total_reserved); _mc_region.print(total_reserved);
@ -1319,13 +1333,25 @@ void VM_PopulateDumpSharedSpace::print_region_stats() {
_ro_region.print(total_reserved); _ro_region.print(total_reserved);
_md_region.print(total_reserved); _md_region.print(total_reserved);
_od_region.print(total_reserved); _od_region.print(total_reserved);
_s0_region.print(total_reserved); print_heap_region_stats(_string_regions, "st", total_reserved);
_s1_region.print(total_reserved); print_heap_region_stats(_open_archive_heap_regions, "oa", total_reserved);
tty->print_cr("total : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]", tty->print_cr("total : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]",
total_bytes, total_reserved, total_u_perc); total_bytes, total_reserved, total_u_perc);
} }
void VM_PopulateDumpSharedSpace::print_heap_region_stats(GrowableArray<MemRegion> *heap_mem,
const char *name, const size_t total_size) {
int arr_len = heap_mem == NULL ? 0 : heap_mem->length();
for (int i = 0; i < arr_len; i++) {
char* start = (char*)heap_mem->at(i).start();
size_t size = heap_mem->at(i).byte_size();
char* top = start + size;
tty->print_cr("%s%d space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [100%% used] at " INTPTR_FORMAT,
name, i, size, size/double(total_size)*100.0, size, p2i(start));
}
}
// Update a Java object to point its Klass* to the new location after // Update a Java object to point its Klass* to the new location after
// shared archive has been compacted. // shared archive has been compacted.
@ -1352,6 +1378,8 @@ class LinkSharedClassesClosure : public KlassClosure {
// to -Xverify setting. // to -Xverify setting.
_made_progress |= MetaspaceShared::try_link_class(ik, THREAD); _made_progress |= MetaspaceShared::try_link_class(ik, THREAD);
guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class"); guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class");
ik->constants()->resolve_class_constants(THREAD);
} }
} }
}; };
@ -1556,6 +1584,98 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) {
} }
} }
#if INCLUDE_CDS_JAVA_HEAP
void VM_PopulateDumpSharedSpace::dump_java_heap_objects() {
if (!MetaspaceShared::is_heap_object_archiving_allowed()) {
if (log_is_enabled(Info, cds)) {
log_info(cds)(
"Archived java heap is not supported as UseG1GC, "
"UseCompressedOops and UseCompressedClassPointers are required."
"Current settings: UseG1GC=%s, UseCompressedOops=%s, UseCompressedClassPointers=%s.",
BOOL_TO_STR(UseG1GC), BOOL_TO_STR(UseCompressedOops),
BOOL_TO_STR(UseCompressedClassPointers));
}
return;
}
{
NoSafepointVerifier nsv;
// Cache for recording where the archived objects are copied to
MetaspaceShared::create_archive_object_cache();
tty->print_cr("Dumping String objects to closed archive heap region ...");
NOT_PRODUCT(StringTable::verify());
// The string space has maximum two regions. See FileMapInfo::write_archive_heap_regions() for details.
_string_regions = new GrowableArray<MemRegion>(2);
StringTable::write_to_archive(_string_regions);
tty->print_cr("Dumping objects to open archive heap region ...");
_open_archive_heap_regions = new GrowableArray<MemRegion>(2);
MetaspaceShared::dump_open_archive_heap_objects(_open_archive_heap_regions);
}
G1HeapVerifier::verify_archive_regions();
}
void MetaspaceShared::dump_open_archive_heap_objects(
GrowableArray<MemRegion> * open_archive) {
assert(UseG1GC, "Only support G1 GC");
assert(UseCompressedOops && UseCompressedClassPointers,
"Only support UseCompressedOops and UseCompressedClassPointers enabled");
Thread* THREAD = Thread::current();
G1CollectedHeap::heap()->begin_archive_alloc_range(true /* open */);
MetaspaceShared::archive_resolved_constants(THREAD);
G1CollectedHeap::heap()->end_archive_alloc_range(open_archive,
os::vm_allocation_granularity());
}
MetaspaceShared::ArchivedObjectCache* MetaspaceShared::_archive_object_cache = NULL;
oop MetaspaceShared::archive_heap_object(oop obj, Thread* THREAD) {
assert(DumpSharedSpaces, "dump-time only");
ArchivedObjectCache* cache = MetaspaceShared::archive_object_cache();
oop* p = cache->get(obj);
if (p != NULL) {
// already archived
return *p;
}
int len = obj->size();
if (G1CollectedHeap::heap()->is_archive_alloc_too_large(len)) {
return NULL;
}
int hash = obj->identity_hash();
oop archived_oop = (oop)G1CollectedHeap::heap()->archive_mem_allocate(len);
if (archived_oop != NULL) {
Copy::aligned_disjoint_words((HeapWord*)obj, (HeapWord*)archived_oop, len);
relocate_klass_ptr(archived_oop);
cache->put(obj, archived_oop);
}
return archived_oop;
}
void MetaspaceShared::archive_resolved_constants(Thread* THREAD) {
int i;
for (i = 0; i < _global_klass_objects->length(); i++) {
Klass* k = _global_klass_objects->at(i);
if (k->is_instance_klass()) {
InstanceKlass* ik = InstanceKlass::cast(k);
ik->constants()->archive_resolved_references(THREAD);
}
}
}
void MetaspaceShared::fixup_mapped_heap_regions() {
FileMapInfo *mapinfo = FileMapInfo::current_info();
mapinfo->fixup_mapped_heap_regions();
}
#endif // INCLUDE_CDS_JAVA_HEAP
// Closure for serializing initialization data in from a data area // Closure for serializing initialization data in from a data area
// (ptr_array) read from the shared file. // (ptr_array) read from the shared file.
@ -1743,11 +1863,6 @@ void MetaspaceShared::initialize_shared_spaces() {
} }
} }
void MetaspaceShared::fixup_shared_string_regions() {
FileMapInfo *mapinfo = FileMapInfo::current_info();
mapinfo->fixup_string_regions();
}
// JVM/TI RedefineClasses() support: // JVM/TI RedefineClasses() support:
bool MetaspaceShared::remap_shared_readonly_as_readwrite() { bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");

View file

@ -31,6 +31,7 @@
#include "memory/virtualspace.hpp" #include "memory/virtualspace.hpp"
#include "utilities/exceptions.hpp" #include "utilities/exceptions.hpp"
#include "utilities/macros.hpp" #include "utilities/macros.hpp"
#include "utilities/resourceHash.hpp"
#define MAX_SHARED_DELTA (0x7FFFFFFF) #define MAX_SHARED_DELTA (0x7FFFFFFF)
@ -56,23 +57,32 @@ class MetaspaceShared : AllStatic {
static bool _has_error_classes; static bool _has_error_classes;
static bool _archive_loading_failed; static bool _archive_loading_failed;
static bool _remapped_readwrite; static bool _remapped_readwrite;
static bool _open_archive_heap_region_mapped;
static address _cds_i2i_entry_code_buffers; static address _cds_i2i_entry_code_buffers;
static size_t _cds_i2i_entry_code_buffers_size; static size_t _cds_i2i_entry_code_buffers_size;
static size_t _core_spaces_size; static size_t _core_spaces_size;
public: public:
enum { enum {
// core archive spaces
mc = 0, // miscellaneous code for method trampolines mc = 0, // miscellaneous code for method trampolines
rw = 1, // read-write shared space in the heap rw = 1, // read-write shared space in the heap
ro = 2, // read-only shared space in the heap ro = 2, // read-only shared space in the heap
md = 3, // miscellaneous data for initializing tables, etc. md = 3, // miscellaneous data for initializing tables, etc.
max_strings = 2, // max number of string regions in string space num_core_spaces = 4, // number of non-string regions
num_non_strings = 4, // number of non-string regions
first_string = num_non_strings, // index of first string region // optional mapped spaces
// The optional data region is the last region.
// Currently it only contains class file data. // Currently it only contains class file data.
od = max_strings + num_non_strings, od = num_core_spaces,
last_valid_region = od, num_non_heap_spaces = od + 1,
n_regions = od + 1 // total number of regions
// mapped java heap regions
first_string = od + 1, // index of first string region
max_strings = 2, // max number of string regions in string space
first_open_archive_heap_region = first_string + max_strings,
max_open_archive_heap_region = 2,
last_valid_region = first_open_archive_heap_region + max_open_archive_heap_region - 1,
n_regions = last_valid_region + 1 // total number of regions
}; };
static void prepare_for_dumping() NOT_CDS_RETURN; static void prepare_for_dumping() NOT_CDS_RETURN;
@ -80,6 +90,45 @@ class MetaspaceShared : AllStatic {
static int preload_classes(const char * class_list_path, static int preload_classes(const char * class_list_path,
TRAPS) NOT_CDS_RETURN_(0); TRAPS) NOT_CDS_RETURN_(0);
#if INCLUDE_CDS_JAVA_HEAP
private:
static bool obj_equals(oop const& p1, oop const& p2) {
return p1 == p2;
}
static unsigned obj_hash(oop const& p) {
unsigned hash = (unsigned)((uintptr_t)&p);
return hash ^ (hash >> LogMinObjAlignment);
}
typedef ResourceHashtable<oop, oop,
MetaspaceShared::obj_hash, MetaspaceShared::obj_equals> ArchivedObjectCache;
static ArchivedObjectCache* _archive_object_cache;
public:
static ArchivedObjectCache* archive_object_cache() {
return _archive_object_cache;
}
static oop archive_heap_object(oop obj, Thread* THREAD);
static void archive_resolved_constants(Thread* THREAD);
#endif
static bool is_heap_object_archiving_allowed() {
CDS_JAVA_HEAP_ONLY(return (UseG1GC && UseCompressedOops && UseCompressedClassPointers);)
NOT_CDS_JAVA_HEAP(return false;)
}
static void create_archive_object_cache() {
CDS_JAVA_HEAP_ONLY(_archive_object_cache = new ArchivedObjectCache(););
}
static void fixup_mapped_heap_regions() NOT_CDS_JAVA_HEAP_RETURN;
static void dump_open_archive_heap_objects(GrowableArray<MemRegion> * open_archive) NOT_CDS_JAVA_HEAP_RETURN;
static void set_open_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(_open_archive_heap_region_mapped = true);
NOT_CDS_JAVA_HEAP_RETURN;
}
static bool open_archive_heap_region_mapped() {
CDS_JAVA_HEAP_ONLY(return _open_archive_heap_region_mapped);
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
static ReservedSpace* shared_rs() { static ReservedSpace* shared_rs() {
CDS_ONLY(return &_shared_rs); CDS_ONLY(return &_shared_rs);
NOT_CDS(return NULL); NOT_CDS(return NULL);
@ -104,16 +153,29 @@ class MetaspaceShared : AllStatic {
} }
static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false); static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false);
static void initialize_shared_spaces() NOT_CDS_RETURN; 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. // 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_in_shared_space(const void* p) NOT_CDS_RETURN_(false);
// Return true if given address is in the shared region corresponding to the idx // Return true if given address is in the shared region corresponding to the idx
static bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false); static bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false);
static bool is_heap_region(int idx) {
CDS_JAVA_HEAP_ONLY(return (idx >= MetaspaceShared::first_string &&
idx < MetaspaceShared::first_open_archive_heap_region +
MetaspaceShared::max_open_archive_heap_region));
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
static bool is_string_region(int idx) { static bool is_string_region(int idx) {
CDS_ONLY(return (idx >= first_string && idx < first_string + max_strings)); CDS_JAVA_HEAP_ONLY(return (idx >= MetaspaceShared::first_string &&
NOT_CDS(return false); idx < MetaspaceShared::first_string + MetaspaceShared::max_strings));
NOT_CDS_JAVA_HEAP_RETURN_(false);
}
static bool is_open_archive_heap_region(int idx) {
CDS_JAVA_HEAP_ONLY(return (idx >= MetaspaceShared::first_open_archive_heap_region &&
idx < MetaspaceShared::first_open_archive_heap_region +
MetaspaceShared::max_open_archive_heap_region));
NOT_CDS_JAVA_HEAP_RETURN_(false);
} }
static bool is_in_trampoline_frame(address addr) NOT_CDS_RETURN_(false); static bool is_in_trampoline_frame(address addr) NOT_CDS_RETURN_(false);
@ -124,7 +186,6 @@ class MetaspaceShared : AllStatic {
static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false); static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false);
static void serialize(SerializeClosure* sc); static void serialize(SerializeClosure* sc);
static MetaspaceSharedStats* stats() { static MetaspaceSharedStats* stats() {
return &_stats; return &_stats;
} }

View file

@ -370,7 +370,7 @@ void Universe::genesis(TRAPS) {
SystemDictionary::Cloneable_klass(), "u3"); SystemDictionary::Cloneable_klass(), "u3");
assert(_the_array_interfaces_array->at(1) == assert(_the_array_interfaces_array->at(1) ==
SystemDictionary::Serializable_klass(), "u3"); SystemDictionary::Serializable_klass(), "u3");
MetaspaceShared::fixup_shared_string_regions(); MetaspaceShared::fixup_mapped_heap_regions();
} else } else
#endif #endif
{ {

View file

@ -33,6 +33,7 @@
#include "memory/heapInspection.hpp" #include "memory/heapInspection.hpp"
#include "memory/metadataFactory.hpp" #include "memory/metadataFactory.hpp"
#include "memory/metaspaceClosure.hpp" #include "memory/metaspaceClosure.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/oopFactory.hpp" #include "memory/oopFactory.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/constantPool.hpp" #include "oops/constantPool.hpp"
@ -47,6 +48,9 @@
#include "runtime/signature.hpp" #include "runtime/signature.hpp"
#include "runtime/vframe.hpp" #include "runtime/vframe.hpp"
#include "utilities/copy.hpp" #include "utilities/copy.hpp"
#if INCLUDE_ALL_GCS
#include "gc/g1/g1SATBCardTableModRefBS.hpp"
#endif // INCLUDE_ALL_GCS
ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) { ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, TRAPS) {
Array<u1>* tags = MetadataFactory::new_array<u1>(loader_data, length, 0, CHECK_NULL); Array<u1>* tags = MetadataFactory::new_array<u1>(loader_data, length, 0, CHECK_NULL);
@ -238,11 +242,53 @@ void ConstantPool::klass_at_put(int class_index, Klass* k) {
release_tag_at_put(class_index, JVM_CONSTANT_Class); release_tag_at_put(class_index, JVM_CONSTANT_Class);
} }
#if INCLUDE_CDS_JAVA_HEAP
// Archive the resolved references
void ConstantPool::archive_resolved_references(Thread* THREAD) {
if (_cache == NULL) {
return; // nothing to do
}
InstanceKlass *ik = pool_holder();
if (!(ik->is_shared_boot_class() || ik->is_shared_platform_class() ||
ik->is_shared_app_class())) {
// Archiving resolved references for classes from non-builtin loaders
// is not yet supported.
set_resolved_references(NULL);
return;
}
objArrayOop rr = resolved_references();
if (rr != NULL) {
for (int i = 0; i < rr->length(); i++) {
oop p = rr->obj_at(i);
if (p != NULL) {
int index = object_to_cp_index(i);
if (tag_at(index).is_string()) {
oop op = StringTable::create_archived_string(p, THREAD);
// If the String object is not archived (possibly too large),
// NULL is returned. Also set it in the array, so we won't
// have a 'bad' reference in the archived resolved_reference
// array.
rr->obj_at_put(i, op);
} else {
rr->obj_at_put(i, NULL);
}
}
}
oop archived = MetaspaceShared::archive_heap_object(rr, THREAD);
_cache->set_archived_references(archived);
set_resolved_references(NULL);
}
}
#endif
// CDS support. Create a new resolved_references array. // CDS support. Create a new resolved_references array.
void ConstantPool::restore_unshareable_info(TRAPS) { void ConstantPool::restore_unshareable_info(TRAPS) {
assert(is_constantPool(), "ensure C++ vtable is restored"); assert(is_constantPool(), "ensure C++ vtable is restored");
assert(on_stack(), "should always be set for shared constant pools"); assert(on_stack(), "should always be set for shared constant pools");
assert(is_shared(), "should always be set for shared constant pools"); assert(is_shared(), "should always be set for shared constant pools");
assert(_cache != NULL, "constant pool _cache should not be NULL");
// Only create the new resolved references array if it hasn't been attempted before // Only create the new resolved references array if it hasn't been attempted before
if (resolved_references() != NULL) return; if (resolved_references() != NULL) return;
@ -251,25 +297,49 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
restore_vtable(); restore_vtable();
if (SystemDictionary::Object_klass_loaded()) { if (SystemDictionary::Object_klass_loaded()) {
ClassLoaderData* loader_data = pool_holder()->class_loader_data();
#if INCLUDE_CDS_JAVA_HEAP
if (MetaspaceShared::open_archive_heap_region_mapped() &&
_cache->archived_references() != NULL) {
oop archived = _cache->archived_references();
// Make sure GC knows the cached object is now live. This is necessary after
// initial GC marking and during concurrent marking as strong roots are only
// scanned during initial marking (at the start of the GC marking).
assert(UseG1GC, "Requires G1 GC");
G1SATBCardTableModRefBS::enqueue(archived);
// Create handle for the archived resolved reference array object
Handle refs_handle(THREAD, (oop)archived);
set_resolved_references(loader_data->add_handle(refs_handle));
} else
#endif
{
// No mapped archived resolved reference array
// Recreate the object array and add to ClassLoaderData. // Recreate the object array and add to ClassLoaderData.
int map_length = resolved_reference_length(); int map_length = resolved_reference_length();
if (map_length > 0) { if (map_length > 0) {
objArrayOop stom = oopFactory::new_objArray(SystemDictionary::Object_klass(), map_length, CHECK); objArrayOop stom = oopFactory::new_objArray(SystemDictionary::Object_klass(), map_length, CHECK);
Handle refs_handle (THREAD, (oop)stom); // must handleize. Handle refs_handle(THREAD, (oop)stom); // must handleize.
ClassLoaderData* loader_data = pool_holder()->class_loader_data();
set_resolved_references(loader_data->add_handle(refs_handle)); set_resolved_references(loader_data->add_handle(refs_handle));
} }
} }
}
} }
void ConstantPool::remove_unshareable_info() { void ConstantPool::remove_unshareable_info() {
// Resolved references are not in the shared archive. // Resolved references are not in the shared archive.
// Save the length for restoration. It is not necessarily the same length // Save the length for restoration. It is not necessarily the same length
// as reference_map.length() if invokedynamic is saved. // as reference_map.length() if invokedynamic is saved. It is needed when
// re-creating the resolved reference array if archived heap data cannot be map
// at runtime.
set_resolved_reference_length( set_resolved_reference_length(
resolved_references() != NULL ? resolved_references()->length() : 0); resolved_references() != NULL ? resolved_references()->length() : 0);
// If archiving heap objects is not allowed, clear the resolved references.
// Otherwise, it is cleared after the resolved references array is cached
// (see archive_resolved_references()).
if (!MetaspaceShared::is_heap_object_archiving_allowed()) {
set_resolved_references(NULL); set_resolved_references(NULL);
}
// Shared ConstantPools are in the RO region, so the _flags cannot be modified. // Shared ConstantPools are in the RO region, so the _flags cannot be modified.
// The _on_stack flag is used to prevent ConstantPools from deallocation during // The _on_stack flag is used to prevent ConstantPools from deallocation during
@ -619,19 +689,19 @@ void ConstantPool::resolve_string_constants_impl(const constantPoolHandle& this_
} }
} }
// Resolve all the classes in the constant pool. If they are all resolved,
// the constant pool is read-only. Enhancement: allocate cp entries to
// another metaspace, and copy to read-only or read-write space if this
// bit is set.
bool ConstantPool::resolve_class_constants(TRAPS) { bool ConstantPool::resolve_class_constants(TRAPS) {
constantPoolHandle cp(THREAD, this); constantPoolHandle cp(THREAD, this);
for (int index = 1; index < length(); index++) { // Index 0 is unused for (int index = 1; index < length(); index++) { // Index 0 is unused
if (tag_at(index).is_unresolved_klass() && if (tag_at(index).is_string()) {
klass_at_if_loaded(cp, index) == NULL) { Symbol* sym = cp->unresolved_string_at(index);
return false; // Look up only. Only resolve references to already interned strings.
oop str = StringTable::lookup(sym);
if (str != NULL) {
int cache_index = cp->cp_to_object_index(index);
cp->string_at_put(index, cache_index, str);
}
} }
} }
// set_preresolution(); or some bit for future use
return true; return true;
} }

View file

@ -714,6 +714,7 @@ class ConstantPool : public Metadata {
} }
// CDS support // CDS support
void archive_resolved_references(Thread *THREAD) NOT_CDS_JAVA_HEAP_RETURN;
void remove_unshareable_info(); void remove_unshareable_info();
void restore_unshareable_info(TRAPS); void restore_unshareable_info(TRAPS);
bool resolve_class_constants(TRAPS); bool resolve_class_constants(TRAPS);

View file

@ -608,6 +608,18 @@ void ConstantPoolCache::initialize(const intArray& inverse_index_map,
} }
} }
#if INCLUDE_CDS_JAVA_HEAP
oop ConstantPoolCache::archived_references() {
assert(UseSharedSpaces, "UseSharedSpaces expected.");
return oopDesc::decode_heap_oop(_archived_references);
}
void ConstantPoolCache::set_archived_references(oop o) {
assert(DumpSharedSpaces, "called only during runtime");
_archived_references = oopDesc::encode_heap_oop(o);
}
#endif
#if INCLUDE_JVMTI #if INCLUDE_JVMTI
// RedefineClasses() API support: // RedefineClasses() API support:
// If any entry of this ConstantPoolCache points to any of // If any entry of this ConstantPoolCache points to any of

View file

@ -415,6 +415,9 @@ class ConstantPoolCache: public MetaspaceObj {
// object index to original constant pool index // object index to original constant pool index
jobject _resolved_references; jobject _resolved_references;
Array<u2>* _reference_map; Array<u2>* _reference_map;
// The narrowOop pointer to the archived resolved_references. Set at CDS dump
// time when caching java heap object is supported.
CDS_JAVA_HEAP_ONLY(narrowOop _archived_references;)
// Sizing // Sizing
debug_only(friend class ClassVerifier;) debug_only(friend class ClassVerifier;)
@ -426,6 +429,7 @@ class ConstantPoolCache: public MetaspaceObj {
const intStack& invokedynamic_references_map) : const intStack& invokedynamic_references_map) :
_length(length), _length(length),
_constant_pool(NULL) { _constant_pool(NULL) {
CDS_JAVA_HEAP_ONLY(_archived_references = 0;)
initialize(inverse_index_map, invokedynamic_inverse_index_map, initialize(inverse_index_map, invokedynamic_inverse_index_map,
invokedynamic_references_map); invokedynamic_references_map);
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
@ -448,6 +452,9 @@ class ConstantPoolCache: public MetaspaceObj {
void metaspace_pointers_do(MetaspaceClosure* it); void metaspace_pointers_do(MetaspaceClosure* it);
MetaspaceObj::Type type() const { return ConstantPoolCacheType; } MetaspaceObj::Type type() const { return ConstantPoolCacheType; }
oop archived_references() NOT_CDS_JAVA_HEAP_RETURN_(NULL);
void set_archived_references(oop o) NOT_CDS_JAVA_HEAP_RETURN;
jobject resolved_references() { return _resolved_references; } jobject resolved_references() { return _resolved_references; }
void set_resolved_references(jobject s) { _resolved_references = s; } void set_resolved_references(jobject s) { _resolved_references = s; }
Array<u2>* reference_map() const { return _reference_map; } Array<u2>* reference_map() const { return _reference_map; }

View file

@ -31,6 +31,9 @@
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/thread.inline.hpp" #include "runtime/thread.inline.hpp"
#include "utilities/copy.hpp" #include "utilities/copy.hpp"
#if INCLUDE_ALL_GCS
#include "gc/g1/g1Allocator.inline.hpp"
#endif
bool always_do_update_barrier = false; bool always_do_update_barrier = false;
@ -138,3 +141,9 @@ bool oopDesc::has_klass_gap() {
// Only has a klass gap when compressed class pointers are used. // Only has a klass gap when compressed class pointers are used.
return UseCompressedClassPointers; return UseCompressedClassPointers;
} }
#if INCLUDE_CDS_JAVA_HEAP
bool oopDesc::is_archive_object(oop p) {
return (p == NULL) ? false : G1ArchiveAllocator::is_archive_object(p);
}
#endif

View file

@ -389,6 +389,8 @@ class oopDesc {
assert(has_klass_gap(), "only applicable to compressed klass pointers"); assert(has_klass_gap(), "only applicable to compressed klass pointers");
return klass_offset_in_bytes() + sizeof(narrowKlass); return klass_offset_in_bytes() + sizeof(narrowKlass);
} }
static bool is_archive_object(oop p) NOT_CDS_JAVA_HEAP_RETURN_(false);
}; };
#endif // SHARE_VM_OOPS_OOP_HPP #endif // SHARE_VM_OOPS_OOP_HPP

View file

@ -590,6 +590,9 @@ void oopDesc::forward_to(oop p) {
"forwarding to something not aligned"); "forwarding to something not aligned");
assert(Universe::heap()->is_in_reserved(p), assert(Universe::heap()->is_in_reserved(p),
"forwarding to something not in heap"); "forwarding to something not in heap");
assert(!is_archive_object(oop(this)) &&
!is_archive_object(p),
"forwarding archive object");
markOop m = markOopDesc::encode_pointer_as_mark(p); markOop m = markOopDesc::encode_pointer_as_mark(p);
assert(m->decode_pointer() == p, "encoding must be reversable"); assert(m->decode_pointer() == p, "encoding must be reversable");
set_mark(m); set_mark(m);

View file

@ -1689,7 +1689,7 @@ WB_END
WB_ENTRY(jboolean, WB_IsShared(JNIEnv* env, jobject wb, jobject obj)) WB_ENTRY(jboolean, WB_IsShared(JNIEnv* env, jobject wb, jobject obj))
oop obj_oop = JNIHandles::resolve(obj); oop obj_oop = JNIHandles::resolve(obj);
return MetaspaceShared::is_in_shared_space((void*)obj_oop); return oopDesc::is_archive_object(obj_oop);
WB_END WB_END
WB_ENTRY(jboolean, WB_IsSharedClass(JNIEnv* env, jobject wb, jclass clazz)) WB_ENTRY(jboolean, WB_IsSharedClass(JNIEnv* env, jobject wb, jclass clazz))
@ -1697,7 +1697,19 @@ WB_ENTRY(jboolean, WB_IsSharedClass(JNIEnv* env, jobject wb, jclass clazz))
WB_END WB_END
WB_ENTRY(jboolean, WB_AreSharedStringsIgnored(JNIEnv* env)) WB_ENTRY(jboolean, WB_AreSharedStringsIgnored(JNIEnv* env))
return StringTable::shared_string_ignored(); return !StringTable::shared_string_mapped();
WB_END
WB_ENTRY(jobject, WB_GetResolvedReferences(JNIEnv* env, jobject wb, jclass clazz))
Klass *k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz));
if (k->is_instance_klass()) {
InstanceKlass *ik = InstanceKlass::cast(k);
ConstantPool *cp = ik->constants();
objArrayOop refs = cp->resolved_references();
return (jobject)JNIHandles::make_local(env, refs);
} else {
return NULL;
}
WB_END WB_END
WB_ENTRY(jboolean, WB_IsCDSIncludedInVmBuild(JNIEnv* env)) WB_ENTRY(jboolean, WB_IsCDSIncludedInVmBuild(JNIEnv* env))
@ -2001,6 +2013,7 @@ static JNINativeMethod methods[] = {
{CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared }, {CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared },
{CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass }, {CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass },
{CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored }, {CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored },
{CC"getResolvedReferences", CC"(Ljava/lang/Class;)Ljava/lang/Object;", (void*)&WB_GetResolvedReferences},
{CC"isCDSIncludedInVmBuild", CC"()Z", (void*)&WB_IsCDSIncludedInVmBuild }, {CC"isCDSIncludedInVmBuild", CC"()Z", (void*)&WB_IsCDSIncludedInVmBuild },
{CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches }, {CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches },
{CC"addCompilerDirective", CC"(Ljava/lang/String;)I", {CC"addCompilerDirective", CC"(Ljava/lang/String;)I",

View file

@ -536,4 +536,18 @@
non_atomic_decl non_atomic_decl
#endif #endif
#if INCLUDE_CDS && INCLUDE_ALL_GCS && defined(_LP64) && !defined(_WINDOWS)
#define INCLUDE_CDS_JAVA_HEAP 1
#define CDS_JAVA_HEAP_ONLY(x) x
#define NOT_CDS_JAVA_HEAP(x)
#define NOT_CDS_JAVA_HEAP_RETURN
#define NOT_CDS_JAVA_HEAP_RETURN_(code)
#else
#define INCLUDE_CDS_JAVA_HEAP 0
#define CDS_JAVA_HEAP_ONLY(x)
#define NOT_CDS_JAVA_HEAP(x) x
#define NOT_CDS_JAVA_HEAP_RETURN {}
#define NOT_CDS_JAVA_HEAP_RETURN_(code) { return code; }
#endif
#endif // SHARE_VM_UTILITIES_MACROS_HPP #endif // SHARE_VM_UTILITIES_MACROS_HPP