8329431: Improve speed of writing CDS heap objects
Reviewed-by: ccheung, matsaave
This commit is contained in:
parent
47df14590c
commit
bab70193dd
src/hotspot/share/cds
test/hotspot/jtreg/runtime/cds/appcds
@ -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) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user