diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 0f5cee30939..36947f7244b 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, 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 @@ -55,6 +55,7 @@ #include "runtime/fieldDescriptor.inline.hpp" #include "runtime/init.hpp" #include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" #include "runtime/safepointVerifiers.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/copy.hpp" @@ -135,8 +136,11 @@ static ArchivableStaticFieldInfo fmg_open_archive_subgraph_entry_fields[] = { {NULL, NULL}, }; +KlassSubGraphInfo* HeapShared::_default_subgraph_info; GrowableArrayCHeap* HeapShared::_pending_roots = NULL; OopHandle HeapShared::_roots; +OopHandle HeapShared::_scratch_basic_type_mirrors[T_VOID+1]; +KlassToOopHandleTable* HeapShared::_scratch_java_mirror_table = NULL; #ifdef ASSERT bool HeapShared::is_archived_object_during_dumptime(oop p) { @@ -343,21 +347,108 @@ oop HeapShared::archive_object(oop obj) { return archived_oop; } -void HeapShared::archive_klass_objects() { +class KlassToOopHandleTable: public ResourceHashtable { +public: + oop get_oop(Klass* k) { + MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); + OopHandle* handle = get(k); + if (handle != NULL) { + return handle->resolve(); + } else { + return NULL; + } + } + void set_oop(Klass* k, oop o) { + MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); + OopHandle handle(Universe::vm_global(), o); + bool is_new = put(k, handle); + assert(is_new, "cannot set twice"); + } + void remove_oop(Klass* k) { + MutexLocker ml(ScratchObjects_lock, Mutex::_no_safepoint_check_flag); + OopHandle* handle = get(k); + if (handle != NULL) { + handle->release(Universe::vm_global()); + remove(k); + } + } +}; + +void HeapShared::init_scratch_objects(TRAPS) { + for (int i = T_BOOLEAN; i < T_VOID+1; i++) { + BasicType bt = (BasicType)i; + if (!is_reference_type(bt)) { + oop m = java_lang_Class::create_basic_type_mirror(type2name(bt), bt, CHECK); + _scratch_basic_type_mirrors[i] = OopHandle(Universe::vm_global(), m); + } + } + _scratch_java_mirror_table = new (mtClass)KlassToOopHandleTable(); +} + +oop HeapShared::scratch_java_mirror(BasicType t) { + assert((uint)t < T_VOID+1, "range check"); + assert(!is_reference_type(t), "sanity"); + return _scratch_basic_type_mirrors[t].resolve(); +} + +oop HeapShared::scratch_java_mirror(Klass* k) { + return _scratch_java_mirror_table->get_oop(k); +} + +void HeapShared::set_scratch_java_mirror(Klass* k, oop mirror) { + _scratch_java_mirror_table->set_oop(k, mirror); +} + +void HeapShared::remove_scratch_objects(Klass* k) { + _scratch_java_mirror_table->remove_oop(k); +} + +void HeapShared::archive_java_mirrors() { + init_seen_objects_table(); + + for (int i = T_BOOLEAN; i < T_VOID+1; i++) { + BasicType bt = (BasicType)i; + if (!is_reference_type(bt)) { + oop m = _scratch_basic_type_mirrors[i].resolve(); + assert(m != NULL, "sanity"); + oop archived_m = archive_reachable_objects_from(1, _default_subgraph_info, m, /*is_closed_archive=*/ false); + assert(archived_m != NULL, "sanity"); + + log_trace(cds, heap, mirror)( + "Archived %s mirror object from " PTR_FORMAT " ==> " PTR_FORMAT, + type2name(bt), p2i(m), p2i(archived_m)); + + Universe::set_archived_basic_type_mirror_index(bt, append_root(archived_m)); + } + } + GrowableArray* klasses = ArchiveBuilder::current()->klasses(); assert(klasses != NULL, "sanity"); for (int i = 0; i < klasses->length(); i++) { - Klass* k = ArchiveBuilder::get_buffered_klass(klasses->at(i)); + Klass* orig_k = klasses->at(i); + oop m = scratch_java_mirror(orig_k); + if (m != NULL) { + Klass* buffered_k = ArchiveBuilder::get_buffered_klass(orig_k); + oop archived_m = archive_reachable_objects_from(1, _default_subgraph_info, m, /*is_closed_archive=*/ false); + guarantee(archived_m != NULL, "scratch mirrors should not point to any unachivable objects"); + buffered_k->set_archived_java_mirror(append_root(archived_m)); + ResourceMark rm; + log_trace(cds, heap, mirror)( + "Archived %s mirror object from " PTR_FORMAT " ==> " PTR_FORMAT, + buffered_k->external_name(), p2i(m), p2i(archived_m)); - // archive mirror object - java_lang_Class::archive_mirror(k); - - // archive the resolved_referenes array - if (k->is_instance_klass()) { - InstanceKlass* ik = InstanceKlass::cast(k); - ik->constants()->archive_resolved_references(); + // archive the resolved_referenes array + if (buffered_k->is_instance_klass()) { + InstanceKlass* ik = InstanceKlass::cast(buffered_k); + ik->constants()->archive_resolved_references(); + } } } + + delete_seen_objects_table(); } void HeapShared::mark_native_pointers(oop orig_obj, oop archived_obj) { @@ -501,6 +592,8 @@ void HeapShared::archive_objects(GrowableArray* closed_regions, { NoSafepointVerifier nsv; + _default_subgraph_info = init_subgraph_info(vmClasses::Object_klass(), false); + // Cache for recording where the archived objects are copied to create_archived_object_cache(log_is_enabled(Info, cds, map)); @@ -516,6 +609,7 @@ void HeapShared::archive_objects(GrowableArray* closed_regions, copy_open_objects(open_regions); CDSHeapVerifier::verify(); + check_default_subgraph_classes(); } G1HeapVerifier::verify_archive_regions(); @@ -542,9 +636,7 @@ void HeapShared::copy_open_objects(GrowableArray* open_regions) { G1CollectedHeap::heap()->begin_archive_alloc_range(true /* open */); - java_lang_Class::archive_basic_type_mirrors(); - - archive_klass_objects(); + archive_java_mirrors(); archive_object_subgraphs(open_archive_subgraph_entry_fields, false /* is_closed_archive */, @@ -1134,9 +1226,11 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure { log_debug(cds, heap)("(%d) %s[" SIZE_FORMAT "] ==> " PTR_FORMAT " size " SIZE_FORMAT " %s", _level, _orig_referencing_obj->klass()->external_name(), field_delta, p2i(obj), obj->size() * HeapWordSize, obj->klass()->external_name()); - LogTarget(Trace, cds, heap) log; - LogStream out(log); - obj->print_on(&out); + if (log_is_enabled(Trace, cds, heap)) { + LogTarget(Trace, cds, heap) log; + LogStream out(log); + obj->print_on(&out); + } } oop archived = HeapShared::archive_reachable_objects_from( @@ -1213,7 +1307,7 @@ oop HeapShared::archive_reachable_objects_from(int level, // // If you get an error here, you probably made a change in the JDK library that has added a Class // object that is referenced (directly or indirectly) by static fields. - if (java_lang_Class::is_instance(orig_obj)) { + if (java_lang_Class::is_instance(orig_obj) && subgraph_info != _default_subgraph_info) { log_error(cds, heap)("(%d) Unknown java.lang.Class object is in the archived sub-graph", level); os::_exit(1); } @@ -1431,6 +1525,31 @@ void HeapShared::verify_reachable_objects_from(oop obj, bool is_archived) { } #endif +// The "default subgraph" contains special objects (see heapShared.hpp) that +// can be accessed before we load any Java classes (including java/lang/Class). +// Make sure that these are only instances of the very few specific types +// that we can handle. +void HeapShared::check_default_subgraph_classes() { + GrowableArray* klasses = _default_subgraph_info->subgraph_object_klasses(); + int num = klasses->length(); + for (int i = 0; i < num; i++) { + Klass* subgraph_k = klasses->at(i); + if (log_is_enabled(Info, cds, heap)) { + ResourceMark rm; + log_info(cds, heap)( + "Archived object klass (default subgraph %d) => %s", + i, subgraph_k->external_name()); + } + + guarantee(subgraph_k->name()->equals("java/lang/Class") || + subgraph_k->name()->equals("java/lang/String") || + subgraph_k->name()->equals("[Ljava/lang/Object;") || + subgraph_k->name()->equals("[C") || + subgraph_k->name()->equals("[B"), + "default subgraph can have only these objects"); + } +} + HeapShared::SeenObjectsTable* HeapShared::_seen_objects_table = NULL; int HeapShared::_num_new_walked_objs; int HeapShared::_num_new_archived_objs; diff --git a/src/hotspot/share/cds/heapShared.hpp b/src/hotspot/share/cds/heapShared.hpp index c77009e5107..6cef34d8ba3 100644 --- a/src/hotspot/share/cds/heapShared.hpp +++ b/src/hotspot/share/cds/heapShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2023, 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 @@ -43,6 +43,7 @@ class DumpedInternedStrings; class FileMapInfo; class KlassSubGraphInfo; +class KlassToOopHandleTable; class ResourceBitMap; struct ArchivableStaticFieldInfo; @@ -156,6 +157,11 @@ public: } static bool is_subgraph_root_class(InstanceKlass* ik); + + // Scratch objects for archiving Klass::java_mirror() + static oop scratch_java_mirror(BasicType t) NOT_CDS_JAVA_HEAP_RETURN_(NULL); + static oop scratch_java_mirror(Klass* k) NOT_CDS_JAVA_HEAP_RETURN_(NULL); + private: #if INCLUDE_CDS_JAVA_HEAP static bool _disable_writing; @@ -247,6 +253,7 @@ private: InstanceKlass* k, int field_offset) PRODUCT_RETURN; static void verify_reachable_objects_from(oop obj, bool is_archived) PRODUCT_RETURN; static void verify_subgraph_from(oop orig_obj) PRODUCT_RETURN; + static void check_default_subgraph_classes(); static KlassSubGraphInfo* init_subgraph_info(Klass *k, bool is_full_module_graph); static KlassSubGraphInfo* get_subgraph_info(Klass *k); @@ -269,8 +276,17 @@ private: static SeenObjectsTable *_seen_objects_table; + // The "default subgraph" is the root of all archived objects that do not belong to any + // of the classes defined in the _archive_subgraph_entry_fields[] arrays: + // - interned strings + // - Klass::java_mirror() + // - ConstantPool::resolved_references() + static KlassSubGraphInfo* _default_subgraph_info; + static GrowableArrayCHeap* _pending_roots; static OopHandle _roots; + static OopHandle _scratch_basic_type_mirrors[T_VOID+1]; + static KlassToOopHandleTable* _scratch_java_mirror_table; static void init_seen_objects_table() { assert(_seen_objects_table == NULL, "must be"); @@ -358,7 +374,7 @@ private: static oop find_archived_heap_object(oop obj); static oop archive_object(oop obj); - static void archive_klass_objects(); + static void archive_java_mirrors(); static void archive_objects(GrowableArray* closed_regions, GrowableArray* open_regions); @@ -374,6 +390,10 @@ private: static ResourceBitMap calculate_ptrmap(MemRegion region); // marks all the native pointers static void add_to_dumped_interned_strings(oop string); + // Scratch objects for archiving Klass::java_mirror() + static void set_scratch_java_mirror(Klass* k, oop mirror); + static void remove_scratch_objects(Klass* k); + // We use the HeapShared::roots() array to make sure that objects stored in the // archived heap regions are not prematurely collected. These roots include: // @@ -403,6 +423,7 @@ private: #endif // INCLUDE_CDS_JAVA_HEAP public: + static void init_scratch_objects(TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static void run_full_gc_in_vm_thread() NOT_CDS_JAVA_HEAP_RETURN; static bool is_heap_region(int idx) { diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index 4b16bc7e526..6d966208860 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -806,18 +806,6 @@ static void initialize_static_string_field(fieldDescriptor* fd, Handle mirror, T mirror()->obj_field_put(fd->offset(), string); } -#if INCLUDE_CDS_JAVA_HEAP -static void initialize_static_string_field_for_dump(fieldDescriptor* fd, Handle mirror) { - DEBUG_ONLY(assert_valid_static_string_field(fd);) - assert(DumpSharedSpaces, "must be"); - assert(HeapShared::is_archived_object_during_dumptime(mirror()), "must be"); - // Archive the String field and update the pointer. - oop s = mirror()->obj_field(fd->offset()); - oop archived_s = StringTable::create_archived_string(s); - mirror()->obj_field_put(fd->offset(), archived_s); -} -#endif - static void initialize_static_primitive_field(fieldDescriptor* fd, Handle mirror) { assert(fd->has_initial_value(), "caller should have checked this"); BasicType t = fd->field_type(); @@ -864,19 +852,6 @@ static void initialize_static_field(fieldDescriptor* fd, Handle mirror, TRAPS) { } } -#if INCLUDE_CDS_JAVA_HEAP -static void initialize_static_field_for_dump(fieldDescriptor* fd, Handle mirror) { - assert(mirror.not_null() && fd->is_static(), "just checking"); - if (fd->has_initial_value()) { - if (fd->field_type() != T_OBJECT) { - initialize_static_primitive_field(fd, mirror); - } else { - initialize_static_string_field_for_dump(fd, mirror); - } - } -} -#endif - void java_lang_Class::fixup_mirror(Klass* k, TRAPS) { assert(InstanceMirrorKlass::offset_of_static_fields() != 0, "must have been computed already"); @@ -973,6 +948,61 @@ void java_lang_Class::allocate_fixup_lists() { set_fixup_module_field_list(module_list); } +void java_lang_Class::allocate_mirror(Klass* k, bool is_scratch, Handle protection_domain, Handle classData, + Handle& mirror, Handle& comp_mirror, TRAPS) { + // Allocate mirror (java.lang.Class instance) + oop mirror_oop = InstanceMirrorKlass::cast(vmClasses::Class_klass())->allocate_instance(k, CHECK); + mirror = Handle(THREAD, mirror_oop); + + // Setup indirection from mirror->klass + set_klass(mirror(), k); + + InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); + assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); + + set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror())); + + // It might also have a component mirror. This mirror must already exist. + if (k->is_array_klass()) { + if (k->is_typeArray_klass()) { + BasicType type = TypeArrayKlass::cast(k)->element_type(); + if (is_scratch) { + comp_mirror = Handle(THREAD, HeapShared::scratch_java_mirror(type)); + } else { + comp_mirror = Handle(THREAD, Universe::java_mirror(type)); + } + } else { + assert(k->is_objArray_klass(), "Must be"); + Klass* element_klass = ObjArrayKlass::cast(k)->element_klass(); + assert(element_klass != NULL, "Must have an element klass"); + if (is_scratch) { + comp_mirror = Handle(THREAD, HeapShared::scratch_java_mirror(element_klass)); + } else { + comp_mirror = Handle(THREAD, element_klass->java_mirror()); + } + } + assert(comp_mirror() != NULL, "must have a mirror"); + + // Two-way link between the array klass and its component mirror: + // (array_klass) k -> mirror -> component_mirror -> array_klass -> k + set_component_mirror(mirror(), comp_mirror()); + // See below for ordering dependencies between field array_klass in component mirror + // and java_mirror in this klass. + } else { + assert(k->is_instance_klass(), "Must be"); + + initialize_mirror_fields(k, mirror, protection_domain, classData, THREAD); + if (HAS_PENDING_EXCEPTION) { + // If any of the fields throws an exception like OOM remove the klass field + // from the mirror so GC doesn't follow it after the klass has been deallocated. + // This mirror looks like a primitive type, which logically it is because it + // it represents no class. + set_klass(mirror(), NULL); + return; + } + } +} + void java_lang_Class::create_mirror(Klass* k, Handle class_loader, Handle module, Handle protection_domain, Handle classData, TRAPS) { @@ -988,50 +1018,10 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader, // Class_klass has to be loaded because it is used to allocate // the mirror. if (vmClasses::Class_klass_loaded()) { - // Allocate mirror (java.lang.Class instance) - oop mirror_oop = InstanceMirrorKlass::cast(vmClasses::Class_klass())->allocate_instance(k, CHECK); - Handle mirror(THREAD, mirror_oop); + Handle mirror; Handle comp_mirror; - // Setup indirection from mirror->klass - set_klass(mirror(), k); - - InstanceMirrorKlass* mk = InstanceMirrorKlass::cast(mirror->klass()); - assert(oop_size(mirror()) == mk->instance_size(k), "should have been set"); - - set_static_oop_field_count(mirror(), mk->compute_static_oop_field_count(mirror())); - - // It might also have a component mirror. This mirror must already exist. - if (k->is_array_klass()) { - if (k->is_typeArray_klass()) { - BasicType type = TypeArrayKlass::cast(k)->element_type(); - comp_mirror = Handle(THREAD, Universe::java_mirror(type)); - } else { - assert(k->is_objArray_klass(), "Must be"); - Klass* element_klass = ObjArrayKlass::cast(k)->element_klass(); - assert(element_klass != NULL, "Must have an element klass"); - comp_mirror = Handle(THREAD, element_klass->java_mirror()); - } - assert(comp_mirror() != NULL, "must have a mirror"); - - // Two-way link between the array klass and its component mirror: - // (array_klass) k -> mirror -> component_mirror -> array_klass -> k - set_component_mirror(mirror(), comp_mirror()); - // See below for ordering dependencies between field array_klass in component mirror - // and java_mirror in this klass. - } else { - assert(k->is_instance_klass(), "Must be"); - - initialize_mirror_fields(k, mirror, protection_domain, classData, THREAD); - if (HAS_PENDING_EXCEPTION) { - // If any of the fields throws an exception like OOM remove the klass field - // from the mirror so GC doesn't follow it after the klass has been deallocated. - // This mirror looks like a primitive type, which logically it is because it - // it represents no class. - set_klass(mirror(), NULL); - return; - } - } + allocate_mirror(k, /*is_scratch=*/false, protection_domain, classData, mirror, comp_mirror, CHECK); // set the classLoader field in the java_lang_Class instance assert(class_loader() == k->class_loader(), "should be same"); @@ -1050,6 +1040,9 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader, // concurrently doesn't expect a k to have a null java_mirror. release_set_array_klass(comp_mirror(), k); } + if (DumpSharedSpaces) { + create_scratch_mirror(k, CHECK); + } } else { assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized"); fixup_mirror_list()->push(k); @@ -1057,191 +1050,35 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader, } #if INCLUDE_CDS_JAVA_HEAP -// Clears mirror fields. Static final fields with initial values are reloaded -// from constant pool. The object identity hash is in the object header and is -// not affected. -class ResetMirrorField: public FieldClosure { - private: - Handle _m; - - public: - ResetMirrorField(Handle mirror) : _m(mirror) {} - - void do_field(fieldDescriptor* fd) { - assert(DumpSharedSpaces, "dump time only"); - assert(_m.not_null(), "Mirror cannot be NULL"); - - if (fd->is_static() && fd->has_initial_value()) { - initialize_static_field_for_dump(fd, _m); - return; - } - - BasicType ft = fd->field_type(); - switch (ft) { - case T_BYTE: - _m()->byte_field_put(fd->offset(), 0); - break; - case T_CHAR: - _m()->char_field_put(fd->offset(), 0); - break; - case T_DOUBLE: - _m()->double_field_put(fd->offset(), 0); - break; - case T_FLOAT: - _m()->float_field_put(fd->offset(), 0); - break; - case T_INT: - _m()->int_field_put(fd->offset(), 0); - break; - case T_LONG: - _m()->long_field_put(fd->offset(), 0); - break; - case T_SHORT: - _m()->short_field_put(fd->offset(), 0); - break; - case T_BOOLEAN: - _m()->bool_field_put(fd->offset(), false); - break; - case T_ARRAY: - case T_OBJECT: { - // It might be useful to cache the String field, but - // for now just clear out any reference field - oop o = _m()->obj_field(fd->offset()); - _m()->obj_field_put(fd->offset(), NULL); - break; - } - default: - ShouldNotReachHere(); - break; - } - } -}; - -void java_lang_Class::archive_basic_type_mirrors() { - assert(HeapShared::can_write(), "must be"); - - for (int t = T_BOOLEAN; t < T_VOID+1; t++) { - BasicType bt = (BasicType)t; - if (!is_reference_type(bt)) { - oop m = Universe::java_mirror(bt); - assert(m != NULL, "sanity"); - // Update the field at _array_klass_offset to point to the relocated array klass. - oop archived_m = HeapShared::archive_object(m); - assert(archived_m != NULL, "sanity"); - - // Clear the fields. Just to be safe - Klass *k = m->klass(); - Handle archived_mirror_h(Thread::current(), archived_m); - ResetMirrorField reset(archived_mirror_h); - InstanceKlass::cast(k)->do_nonstatic_fields(&reset); - - log_trace(cds, heap, mirror)( - "Archived %s mirror object from " PTR_FORMAT " ==> " PTR_FORMAT, - type2name(bt), p2i(m), p2i(archived_m)); - - Universe::set_archived_basic_type_mirror_index(bt, HeapShared::append_root(archived_m)); - } - } -} +// The "scratch mirror" stores the states of the mirror object that can be +// decided at dump time (such as the initial values of the static fields, the +// component mirror, etc). At runtime, more information is added to it by +// java_lang_Class::restore_archived_mirror(). // -// After the mirror object is successfully archived, the archived -// klass is set with _has_archived_raw_mirror flag. +// Essentially, /*dumptime*/create_scratch_mirror() + /*runtime*/restore_archived_mirror() +// produces the same result as /*runtime*/create_mirror(). // -// The _has_archived_raw_mirror flag is cleared at runtime when the -// archived mirror is restored. If archived java heap data cannot -// be used at runtime, new mirror object is created for the shared -// class. The _has_archived_raw_mirror is cleared also during the process. -oop java_lang_Class::archive_mirror(Klass* k) { - assert(HeapShared::can_write(), "must be"); - - // Mirror is already archived - if (k->has_archived_mirror_index()) { - assert(k->archived_java_mirror() != NULL, "no archived mirror"); - return k->archived_java_mirror(); +// Note: we archive the "scratch mirror" instead of k->java_mirror(), because the +// latter may contain dumptime-specific information that cannot be archived +// (e.g., ClassLoaderData*, or static fields that are modified by Java code execution). +void java_lang_Class::create_scratch_mirror(Klass* k, TRAPS) { + if (k->class_loader() != NULL && + k->class_loader() != SystemDictionary::java_platform_loader() && + k->class_loader() != SystemDictionary::java_system_loader()) { + // We only archive the mirrors of classes loaded by the built-in loaders + return; } - // No mirror - oop mirror = k->java_mirror(); - if (mirror == NULL) { - return NULL; + Handle protection_domain, classData; // set to NULL. Will be reinitialized at runtime + Handle mirror; + Handle comp_mirror; + allocate_mirror(k, /*is_scratch=*/true, protection_domain, classData, mirror, comp_mirror, CHECK); + + if (comp_mirror() != NULL) { + release_set_array_klass(comp_mirror(), k); } - if (k->is_instance_klass()) { - InstanceKlass *ik = InstanceKlass::cast(k); - assert(ik->signers() == NULL, "class with signer should have been excluded"); - - if (!(ik->is_shared_boot_class() || ik->is_shared_platform_class() || - ik->is_shared_app_class())) { - // Archiving mirror for classes from non-builtin loaders is not - // supported. - return NULL; - } - } - - // Now start archiving the mirror object - oop archived_mirror = HeapShared::archive_object(mirror); - if (archived_mirror == NULL) { - return NULL; - } - - archived_mirror = process_archived_mirror(k, mirror, archived_mirror); - if (archived_mirror == NULL) { - return NULL; - } - - k->set_archived_java_mirror(archived_mirror); - - ResourceMark rm; - log_trace(cds, heap, mirror)( - "Archived %s mirror object from " PTR_FORMAT " ==> " PTR_FORMAT, - k->external_name(), p2i(mirror), p2i(archived_mirror)); - - return archived_mirror; -} - -// The process is based on create_mirror(). -oop java_lang_Class::process_archived_mirror(Klass* k, oop mirror, - oop archived_mirror) { - // Clear nonstatic fields in archived mirror. Some of the fields will be set - // to archived metadata and objects below. - Klass *c = archived_mirror->klass(); - Handle archived_mirror_h(Thread::current(), archived_mirror); - ResetMirrorField reset(archived_mirror_h); - InstanceKlass::cast(c)->do_nonstatic_fields(&reset); - - if (k->is_array_klass()) { - oop archived_comp_mirror; - if (k->is_typeArray_klass()) { - // The primitive type mirrors are already archived. Get the archived mirror. - oop comp_mirror = component_mirror(mirror); - archived_comp_mirror = HeapShared::find_archived_heap_object(comp_mirror); - assert(archived_comp_mirror != NULL, "Must be"); - } else { - assert(k->is_objArray_klass(), "Must be"); - Klass* element_klass = ObjArrayKlass::cast(k)->element_klass(); - assert(element_klass != NULL, "Must have an element klass"); - archived_comp_mirror = archive_mirror(element_klass); - if (archived_comp_mirror == NULL) { - return NULL; - } - } - set_component_mirror(archived_mirror, archived_comp_mirror); - } else { - assert(k->is_instance_klass(), "Must be"); - - // Reset local static fields in the mirror - InstanceKlass::cast(k)->do_local_static_fields(&reset); - - set_protection_domain(archived_mirror, NULL); - set_signers(archived_mirror, NULL); - set_source_file(archived_mirror, NULL); - } - - // clear class loader and mirror_module_field - set_class_loader(archived_mirror, NULL); - set_module(archived_mirror, NULL); - - return archived_mirror; + HeapShared::set_scratch_java_mirror(k, mirror()); } // Returns true if the mirror is updated, false if no archived mirror diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp index 39e12710b06..9c0a77b06e3 100644 --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -249,6 +249,8 @@ class java_lang_Class : AllStatic { static void compute_offsets(); // Instance creation + static void allocate_mirror(Klass* k, bool is_scratch, Handle protection_domain, Handle classData, + Handle& mirror, Handle& comp_mirror, TRAPS); // returns mirror and comp_mirror static void create_mirror(Klass* k, Handle class_loader, Handle module, Handle protection_domain, Handle classData, TRAPS); static void fixup_mirror(Klass* k, TRAPS); @@ -256,10 +258,7 @@ class java_lang_Class : AllStatic { // Archiving static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; - static void archive_basic_type_mirrors() NOT_CDS_JAVA_HEAP_RETURN; - static oop archive_mirror(Klass* k) NOT_CDS_JAVA_HEAP_RETURN_(NULL); - static oop process_archived_mirror(Klass* k, oop mirror, oop archived_mirror) - NOT_CDS_JAVA_HEAP_RETURN_(NULL); + static void create_scratch_mirror(Klass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN; static bool restore_archived_mirror(Klass *k, Handle class_loader, Handle module, Handle protection_domain, TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(false); diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 144c3d1535e..99348438a05 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -478,6 +478,9 @@ void Universe::initialize_basic_type_mirrors(TRAPS) { CDS_JAVA_HEAP_ONLY(_archived_basic_type_mirror_indices[i] = -1); } } + if (DumpSharedSpaces) { + HeapShared::init_scratch_objects(CHECK); + } } void Universe::fixup_mirrors(TRAPS) { diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index dade330a248..a2d1e55e1f6 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -692,6 +692,12 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { set_annotations(NULL); SystemDictionaryShared::handle_class_unloading(this); + +#if INCLUDE_CDS_JAVA_HEAP + if (DumpSharedSpaces) { + HeapShared::remove_scratch_objects(this); + } +#endif } bool InstanceKlass::is_record() const { diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 00d55fce477..0d936759f59 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -642,9 +642,9 @@ void Klass::clear_archived_mirror_index() { } // No GC barrier -void Klass::set_archived_java_mirror(oop m) { - assert(DumpSharedSpaces, "called only during runtime"); - _archived_mirror_index = HeapShared::append_root(m); +void Klass::set_archived_java_mirror(int mirror_index) { + assert(DumpSharedSpaces, "called only during dumptime"); + _archived_mirror_index = mirror_index; } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp index d251c998ebf..b2bd5866647 100644 --- a/src/hotspot/share/oops/klass.hpp +++ b/src/hotspot/share/oops/klass.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -266,7 +266,7 @@ protected: void set_java_mirror(Handle m); oop archived_java_mirror() NOT_CDS_JAVA_HEAP_RETURN_(NULL); - void set_archived_java_mirror(oop m) NOT_CDS_JAVA_HEAP_RETURN; + void set_archived_java_mirror(int mirror_index) NOT_CDS_JAVA_HEAP_RETURN; // Temporary mirror switch used by RedefineClasses OopHandle java_mirror_handle() const { return _java_mirror; } diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp index fca953f4570..6ee976fde60 100644 --- a/src/hotspot/share/runtime/mutexLocker.cpp +++ b/src/hotspot/share/runtime/mutexLocker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -155,6 +155,7 @@ Mutex* DumpRegion_lock = NULL; Mutex* ClassListFile_lock = NULL; Mutex* UnregisteredClassesTable_lock= NULL; Mutex* LambdaFormInvokers_lock = NULL; +Mutex* ScratchObjects_lock = NULL; #endif // INCLUDE_CDS Mutex* Bootclasspath_lock = NULL; @@ -329,6 +330,7 @@ void mutex_init() { def(DumpRegion_lock , PaddedMutex , nosafepoint); def(ClassListFile_lock , PaddedMutex , nosafepoint); def(LambdaFormInvokers_lock , PaddedMutex , safepoint); + def(ScratchObjects_lock , PaddedMutex , nosafepoint-1); // Holds DumpTimeTable_lock #endif // INCLUDE_CDS def(Bootclasspath_lock , PaddedMutex , nosafepoint); def(Zip_lock , PaddedMonitor, nosafepoint-1); // Holds DumpTimeTable_lock diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp index 6498867a3fd..e0d9dab2f93 100644 --- a/src/hotspot/share/runtime/mutexLocker.hpp +++ b/src/hotspot/share/runtime/mutexLocker.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2023, 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 @@ -126,6 +126,7 @@ extern Mutex* DumpRegion_lock; // Symbol::operator new(size_t extern Mutex* ClassListFile_lock; // ClassListWriter() extern Mutex* UnregisteredClassesTable_lock; // UnregisteredClassesTableTable extern Mutex* LambdaFormInvokers_lock; // Protecting LambdaFormInvokers::_lambdaform_lines +extern Mutex* ScratchObjects_lock; // Protecting _scratch_xxx_table in heapShared.cpp #endif // INCLUDE_CDS #if INCLUDE_JFR extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table