8329431: Improve speed of writing CDS heap objects

Reviewed-by: ccheung, matsaave
This commit is contained in:
Ioi Lam 2024-04-10 04:14:10 +00:00
parent 47df14590c
commit bab70193dd
6 changed files with 85 additions and 61 deletions

@ -61,7 +61,7 @@ address ArchiveHeapWriter::_requested_top;
GrowableArrayCHeap<ArchiveHeapWriter::NativePointerInfo, mtClassShared>* ArchiveHeapWriter::_native_pointers;
GrowableArrayCHeap<oop, mtClassShared>* ArchiveHeapWriter::_source_objs;
GrowableArrayCHeap<int, mtClassShared>* ArchiveHeapWriter::_source_objs_order;
GrowableArrayCHeap<ArchiveHeapWriter::HeapObjOrder, mtClassShared>* ArchiveHeapWriter::_source_objs_order;
ArchiveHeapWriter::BufferOffsetToSourceObjectTable*
ArchiveHeapWriter::_buffer_offset_to_source_obj_table = nullptr;
@ -78,14 +78,13 @@ void ArchiveHeapWriter::init() {
if (HeapShared::can_write()) {
Universe::heap()->collect(GCCause::_java_lang_system_gc);
_buffer_offset_to_source_obj_table = new BufferOffsetToSourceObjectTable();
_buffer_offset_to_source_obj_table = new BufferOffsetToSourceObjectTable(/*size (prime)*/36137, /*max size*/1 * M);
_fillers = new FillersTable();
_requested_bottom = nullptr;
_requested_top = nullptr;
_native_pointers = new GrowableArrayCHeap<NativePointerInfo, mtClassShared>(2048);
_source_objs = new GrowableArrayCHeap<oop, mtClassShared>(10000);
_source_objs_order = new GrowableArrayCHeap<int, mtClassShared>(10000);
guarantee(UseG1GC, "implementation limitation");
guarantee(MIN_GC_REGION_ALIGNMENT <= /*G1*/HeapRegion::min_region_size_in_words() * HeapWordSize, "must be");
@ -93,7 +92,6 @@ void ArchiveHeapWriter::init() {
}
void ArchiveHeapWriter::add_source_obj(oop src_obj) {
_source_objs_order->append(_source_objs->length());
_source_objs->append(src_obj);
}
@ -230,17 +228,17 @@ void ArchiveHeapWriter::copy_roots_to_buffer(GrowableArrayCHeap<oop, mtClassShar
}
static int oop_sorting_rank(oop o) {
bool has_o_ptr = HeapShared::has_oop_pointers(o);
bool has_n_ptr = HeapShared::has_native_pointers(o);
bool has_oop_ptr, has_native_ptr;
HeapShared::get_pointer_info(o, has_oop_ptr, has_native_ptr);
if (!has_o_ptr) {
if (!has_n_ptr) {
if (!has_oop_ptr) {
if (!has_native_ptr) {
return 0;
} else {
return 1;
}
} else {
if (has_n_ptr) {
if (has_native_ptr) {
return 2;
} else {
return 3;
@ -253,36 +251,46 @@ static int oop_sorting_rank(oop o) {
// - objects that have only native pointers
// - objects that have both native and oop pointers
// - objects that have only oop pointers
int ArchiveHeapWriter::compare_objs_by_oop_fields(int* a, int* b) {
oop oa = _source_objs->at(*a);
oop ob = _source_objs->at(*b);
int rank_a = oop_sorting_rank(oa);
int rank_b = oop_sorting_rank(ob);
int ArchiveHeapWriter::compare_objs_by_oop_fields(HeapObjOrder* a, HeapObjOrder* b) {
int rank_a = a->_rank;
int rank_b = b->_rank;
if (rank_a != rank_b) {
return rank_a - rank_b;
} else {
// If they are the same rank, sort them by their position in the _source_objs array
return *a - *b;
return a->_index - b->_index;
}
}
void ArchiveHeapWriter::sort_source_objs() {
log_info(cds)("sorting heap objects");
int len = _source_objs->length();
_source_objs_order = new GrowableArrayCHeap<HeapObjOrder, mtClassShared>(len);
for (int i = 0; i < len; i++) {
oop o = _source_objs->at(i);
int rank = oop_sorting_rank(o);
HeapObjOrder os = {i, rank};
_source_objs_order->append(os);
}
log_info(cds)("computed ranks");
_source_objs_order->sort(compare_objs_by_oop_fields);
log_info(cds)("sorting heap objects done");
}
void ArchiveHeapWriter::copy_source_objs_to_buffer(GrowableArrayCHeap<oop, mtClassShared>* roots) {
sort_source_objs();
for (int i = 0; i < _source_objs_order->length(); i++) {
int src_obj_index = _source_objs_order->at(i);
int src_obj_index = _source_objs_order->at(i)._index;
oop src_obj = _source_objs->at(src_obj_index);
HeapShared::CachedOopInfo* info = HeapShared::archived_object_cache()->get(src_obj);
assert(info != nullptr, "must be");
size_t buffer_offset = copy_one_source_obj_to_buffer(src_obj);
info->set_buffer_offset(buffer_offset);
_buffer_offset_to_source_obj_table->put(buffer_offset, src_obj);
_buffer_offset_to_source_obj_table->put_when_absent(buffer_offset, src_obj);
_buffer_offset_to_source_obj_table->maybe_grow();
}
copy_roots_to_buffer(roots);
@ -579,7 +587,7 @@ void ArchiveHeapWriter::relocate_embedded_oops(GrowableArrayCHeap<oop, mtClassSh
heap_info->oopmap()->resize(heap_region_byte_size / oopmap_unit);
for (int i = 0; i < _source_objs_order->length(); i++) {
int src_obj_index = _source_objs_order->at(i);
int src_obj_index = _source_objs_order->at(i)._index;
oop src_obj = _source_objs->at(src_obj_index);
HeapShared::CachedOopInfo* info = HeapShared::archived_object_cache()->get(src_obj);
assert(info != nullptr, "must be");

@ -140,10 +140,21 @@ private:
static GrowableArrayCHeap<NativePointerInfo, mtClassShared>* _native_pointers;
static GrowableArrayCHeap<oop, mtClassShared>* _source_objs;
static GrowableArrayCHeap<int, mtClassShared>* _source_objs_order;
typedef ResourceHashtable<size_t, oop,
36137, // prime number
// We sort _source_objs_order to minimize the number of bits in ptrmap and oopmap.
// See comments near the body of ArchiveHeapWriter::compare_objs_by_oop_fields().
// The objects will be written in the order of:
//_source_objs->at(_source_objs_order->at(0)._index)
// source_objs->at(_source_objs_order->at(1)._index)
// source_objs->at(_source_objs_order->at(2)._index)
// ...
struct HeapObjOrder {
int _index; // The location of this object in _source_objs
int _rank; // A lower rank means the object will be written at a lower location.
};
static GrowableArrayCHeap<HeapObjOrder, mtClassShared>* _source_objs_order;
typedef ResizeableResourceHashtable<size_t, oop,
AnyObj::C_HEAP,
mtClassShared> BufferOffsetToSourceObjectTable;
static BufferOffsetToSourceObjectTable* _buffer_offset_to_source_obj_table;
@ -212,7 +223,7 @@ private:
static void update_header_for_requested_obj(oop requested_obj, oop src_obj, Klass* src_klass);
static int compare_objs_by_oop_fields(int* a, int* b);
static int compare_objs_by_oop_fields(HeapObjOrder* a, HeapObjOrder* b);
static void sort_source_objs();
public:

@ -285,7 +285,8 @@ bool HeapShared::archive_object(oop obj) {
// making the archive reproducible.
obj->identity_hash();
CachedOopInfo info = make_cached_oop_info(obj);
archived_object_cache()->put(obj, info);
archived_object_cache()->put_when_absent(obj, info);
archived_object_cache()->maybe_grow();
mark_native_pointers(obj);
if (log_is_enabled(Debug, cds, heap)) {
@ -443,16 +444,11 @@ void HeapShared::mark_native_pointers(oop orig_obj) {
}
}
bool HeapShared::has_oop_pointers(oop src_obj) {
void HeapShared::get_pointer_info(oop src_obj, bool& has_oop_pointers, bool& has_native_pointers) {
CachedOopInfo* info = archived_object_cache()->get(src_obj);
assert(info != nullptr, "must be");
return info->has_oop_pointers();
}
bool HeapShared::has_native_pointers(oop src_obj) {
CachedOopInfo* info = archived_object_cache()->get(src_obj);
assert(info != nullptr, "must be");
return info->has_native_pointers();
has_oop_pointers = info->has_oop_pointers();
has_native_pointers = info->has_native_pointers();
}
void HeapShared::set_has_native_pointers(oop src_obj) {
@ -1424,7 +1420,8 @@ bool HeapShared::has_been_seen_during_subgraph_recording(oop obj) {
void HeapShared::set_has_been_seen_during_subgraph_recording(oop obj) {
assert(!has_been_seen_during_subgraph_recording(obj), "sanity");
_seen_objects_table->put(obj, true);
_seen_objects_table->put_when_absent(obj, true);
_seen_objects_table->maybe_grow();
++ _num_new_walked_objs;
}
@ -1630,7 +1627,7 @@ bool HeapShared::is_a_test_class_in_unnamed_module(Klass* ik) {
void HeapShared::init_for_dumping(TRAPS) {
if (HeapShared::can_write()) {
setup_test_class(ArchiveHeapTestClass);
_dumped_interned_strings = new (mtClass)DumpedInternedStrings();
_dumped_interned_strings = new (mtClass)DumpedInternedStrings(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE);
init_subgraph_entry_fields(CHECK);
}
}
@ -1696,6 +1693,9 @@ void HeapShared::add_to_dumped_interned_strings(oop string) {
assert(!ArchiveHeapWriter::is_string_too_large_to_archive(string), "must be");
bool created;
_dumped_interned_strings->put_if_absent(string, true, &created);
if (created) {
_dumped_interned_strings->maybe_grow();
}
}
#ifndef PRODUCT

@ -215,8 +215,9 @@ private:
static void check_enum_obj(int level, KlassSubGraphInfo* subgraph_info,
oop orig_obj);
typedef ResourceHashtable<oop, CachedOopInfo,
36137, // prime number
static const int INITIAL_TABLE_SIZE = 15889; // prime number
static const int MAX_TABLE_SIZE = 1000000;
typedef ResizeableResourceHashtable<oop, CachedOopInfo,
AnyObj::C_HEAP,
mtClassShared,
HeapShared::oop_hash> ArchivedObjectCache;
@ -277,8 +278,7 @@ private:
// !UseCompressedOops only: used to relocate pointers to the archived objects
static ptrdiff_t _runtime_delta;
typedef ResourceHashtable<oop, bool,
15889, // prime number
typedef ResizeableResourceHashtable<oop, bool,
AnyObj::C_HEAP,
mtClassShared,
HeapShared::oop_hash> SeenObjectsTable;
@ -300,7 +300,7 @@ private:
static void init_seen_objects_table() {
assert(_seen_objects_table == nullptr, "must be");
_seen_objects_table = new (mtClass)SeenObjectsTable();
_seen_objects_table = new (mtClass)SeenObjectsTable(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE);
}
static void delete_seen_objects_table() {
assert(_seen_objects_table != nullptr, "must be");
@ -355,7 +355,7 @@ private:
static void reset_archived_object_states(TRAPS);
static void create_archived_object_cache() {
_archived_object_cache =
new (mtClass)ArchivedObjectCache();
new (mtClass)ArchivedObjectCache(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE);
}
static void destroy_archived_object_cache() {
delete _archived_object_cache;
@ -380,9 +380,8 @@ private:
// Scratch objects for archiving Klass::java_mirror()
static void set_scratch_java_mirror(Klass* k, oop mirror);
static void remove_scratch_objects(Klass* k);
static bool has_oop_pointers(oop obj);
static bool has_native_pointers(oop obj);
static void set_has_native_pointers(oop obj);
static void get_pointer_info(oop src_obj, bool& has_oop_pointers, bool& has_native_pointers);
static void set_has_native_pointers(oop src_obj);
// We use the HeapShared::roots() array to make sure that objects stored in the
// archived heap region are not prematurely collected. These roots include:
@ -436,12 +435,18 @@ private:
#if INCLUDE_CDS_JAVA_HEAP
class DumpedInternedStrings :
public ResourceHashtable<oop, bool,
15889, // prime number
public ResizeableResourceHashtable<oop, bool,
AnyObj::C_HEAP,
mtClassShared,
HeapShared::string_oop_hash>
{};
{
public:
DumpedInternedStrings(unsigned size, unsigned max_size) :
ResizeableResourceHashtable<oop, bool,
AnyObj::C_HEAP,
mtClassShared,
HeapShared::string_oop_hash>(size, max_size) {}
};
#endif
#endif // SHARE_CDS_HEAPSHARED_HPP

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -40,8 +40,9 @@ public class LotsOfClasses {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<>();
long start = System.currentTimeMillis();
TestCommon.findAllClasses(list);
System.out.println("findAllClasses = " + (System.currentTimeMillis() - start) + "ms");
CDSOptions opts = new CDSOptions();
opts.setClassList(list);
opts.addSuffix("--add-modules");

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -678,27 +678,26 @@ public class TestCommon extends CDSTestUtils {
return true;
}
static Pattern pattern;
static void findAllClasses(ArrayList<String> list) throws Exception {
// Find all the classes in the jrt file system
pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class");
Pattern pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class");
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
Path base = fs.getPath("/modules/");
findAllClassesAtPath(base, list);
findAllClassesAtPath(base, pattern, list);
}
private static void findAllClassesAtPath(Path p, ArrayList<String> list) throws Exception {
private static void findAllClassesAtPath(Path p, Pattern pattern, ArrayList<String> list) throws Exception {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
for (Path entry: stream) {
Matcher matcher = pattern.matcher(entry.toString());
if (matcher.find()) {
String className = matcher.group(1);
list.add(className);
if (Files.isDirectory(entry)) {
findAllClassesAtPath(entry, pattern, list);
} else {
Matcher matcher = pattern.matcher(entry.toString());
if (matcher.find()) {
String className = matcher.group(1);
list.add(className);
}
}
try {
findAllClassesAtPath(entry, list);
} catch (Exception ex) {}
}
}
}