diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index afb378a11f2..72fcde628c0 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -30,6 +30,7 @@ #include "cds/dumpAllocStats.hpp" #include "cds/heapShared.hpp" #include "cds/metaspaceShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/classLoaderDataShared.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionaryShared.hpp" @@ -413,6 +414,10 @@ bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* ref, bool read if (src_obj == nullptr) { return false; } + if (RegeneratedClasses::has_been_regenerated(src_obj)) { + // No need to copy it. We will later relocate it to point to the regenerated klass/method. + return false; + } remember_embedded_pointer_in_enclosing_obj(ref); FollowMode follow_mode = get_follow_mode(ref); @@ -425,6 +430,14 @@ bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* ref, bool read } } +#ifdef ASSERT + if (ref->msotype() == MetaspaceObj::MethodType) { + Method* m = (Method*)ref->obj(); + assert(!RegeneratedClasses::has_been_regenerated((address)m->method_holder()), + "Should not archive methods in a class that has been regenerated"); + } +#endif + assert(p->read_only() == src_info.read_only(), "must be"); if (created && src_info.should_copy()) { @@ -439,6 +452,17 @@ bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* ref, bool read } } +void ArchiveBuilder::record_regenerated_object(address orig_src_obj, address regen_src_obj) { + // Record the fact that orig_src_obj has been replaced by regen_src_obj. All calls to get_buffered_addr(orig_src_obj) + // should return the same value as get_buffered_addr(regen_src_obj). + SourceObjInfo* p = _src_obj_table.get(regen_src_obj); + assert(p != nullptr, "regenerated object should always be dumped"); + SourceObjInfo orig_src_info(orig_src_obj, p); + bool created; + _src_obj_table.put_if_absent(orig_src_obj, orig_src_info, &created); + assert(created, "We shouldn't have archived the original copy of a regenerated object"); +} + // Remember that we have a pointer inside ref->enclosing_obj() that points to ref->obj() void ArchiveBuilder::remember_embedded_pointer_in_enclosing_obj(MetaspaceClosure::Ref* ref) { assert(ref->obj() != nullptr, "should have checked"); @@ -583,6 +607,8 @@ void ArchiveBuilder::dump_ro_metadata() { alloc_stats()->record_modules(ro_region()->top() - start, /*read_only*/true); } #endif + + RegeneratedClasses::record_regenerated_objects(); } void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region, @@ -637,7 +663,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s _alloc_stats.record(src_info->msotype(), int(newtop - oldtop), src_info->read_only()); } -// This is used by code that hand-assemble data structures, such as the LambdaProxyClassKey, that are +// This is used by code that hand-assembles data structures, such as the LambdaProxyClassKey, that are // not handled by MetaspaceClosure. void ArchiveBuilder::write_pointer_in_buffer(address* ptr_location, address src_addr) { assert(is_in_buffer_space(ptr_location), "must be"); @@ -652,7 +678,8 @@ void ArchiveBuilder::write_pointer_in_buffer(address* ptr_location, address src_ address ArchiveBuilder::get_buffered_addr(address src_addr) const { SourceObjInfo* p = _src_obj_table.get(src_addr); - assert(p != nullptr, "must be"); + assert(p != nullptr, "src_addr " INTPTR_FORMAT " is used but has not been archived", + p2i(src_addr)); return p->buffered_addr(); } diff --git a/src/hotspot/share/cds/archiveBuilder.hpp b/src/hotspot/share/cds/archiveBuilder.hpp index 8435b7564c3..f69864fdf54 100644 --- a/src/hotspot/share/cds/archiveBuilder.hpp +++ b/src/hotspot/share/cds/archiveBuilder.hpp @@ -143,6 +143,15 @@ private: } } + // This constructor is only used for regenerated objects (created by LambdaFormInvokers, etc). + // src = address of a Method or InstanceKlass that has been regenerated. + // renegerated_obj_info = info for the regenerated version of src. + SourceObjInfo(address src, SourceObjInfo* renegerated_obj_info) : + _ptrmap_start(0), _ptrmap_end(0), _read_only(false), + _follow_mode(renegerated_obj_info->_follow_mode), + _size_in_bytes(0), _msotype(renegerated_obj_info->_msotype), + _source_addr(src), _buffered_addr(renegerated_obj_info->_buffered_addr) {} + bool should_copy() const { return _follow_mode == make_a_copy; } void set_buffered_addr(address addr) { assert(should_copy(), "must be"); @@ -368,6 +377,7 @@ public: void dump_rw_metadata(); void dump_ro_metadata(); void relocate_metaspaceobj_embedded_pointers(); + void record_regenerated_object(address orig_src_obj, address regen_src_obj); void make_klasses_shareable(); void relocate_to_requested(); void write_archive(FileMapInfo* mapinfo, ArchiveHeapInfo* heap_info); diff --git a/src/hotspot/share/cds/dynamicArchive.cpp b/src/hotspot/share/cds/dynamicArchive.cpp index e2754a839bd..56967897e62 100644 --- a/src/hotspot/share/cds/dynamicArchive.cpp +++ b/src/hotspot/share/cds/dynamicArchive.cpp @@ -29,8 +29,7 @@ #include "cds/cds_globals.hpp" #include "cds/classPrelinker.hpp" #include "cds/dynamicArchive.hpp" -#include "cds/lambdaFormInvokers.hpp" -#include "cds/metaspaceShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionaryShared.hpp" @@ -346,7 +345,7 @@ public: _builder.doit(); } ~VM_PopulateDynamicDumpSharedSpace() { - LambdaFormInvokers::cleanup_regenerated_classes(); + RegeneratedClasses::cleanup(); } }; diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index 70a9017977b..fec81184470 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -26,6 +26,7 @@ #include "cds/archiveBuilder.hpp" #include "cds/lambdaFormInvokers.hpp" #include "cds/metaspaceShared.hpp" +#include "cds/regeneratedClasses.hpp" #include "classfile/classLoadInfo.hpp" #include "classfile/classFileStream.hpp" #include "classfile/javaClasses.inline.hpp" @@ -51,7 +52,6 @@ GrowableArrayCHeap* LambdaFormInvokers::_lambdaform_lines = nullptr; Array*>* LambdaFormInvokers::_static_archive_invokers = nullptr; -GrowableArrayCHeap* LambdaFormInvokers::_regenerated_mirrors = nullptr; #define NUM_FILTER 4 static const char* filter[NUM_FILTER] = {"java.lang.invoke.Invokers$Holder", @@ -77,24 +77,6 @@ void LambdaFormInvokers::append(char* line) { _lambdaform_lines->append(line); } -// The regenerated Klass is not added to any class loader, so we need -// to keep its java_mirror alive to avoid class unloading. -void LambdaFormInvokers::add_regenerated_class(oop regenerated_class) { - if (_regenerated_mirrors == nullptr) { - _regenerated_mirrors = new GrowableArrayCHeap(150); - } - _regenerated_mirrors->append(OopHandle(Universe::vm_global(), regenerated_class)); -} - -void LambdaFormInvokers::cleanup_regenerated_classes() { - if (_regenerated_mirrors == nullptr) return; - - for (int i = 0; i < _regenerated_mirrors->length(); i++) { - _regenerated_mirrors->at(i).release(Universe::vm_global()); - } - delete _regenerated_mirrors; - _regenerated_mirrors = nullptr; -} // convenient output class PrintLambdaFormMessage { @@ -207,7 +189,7 @@ void LambdaFormInvokers::regenerate_class(char* class_name, ClassFileStream& st, CHECK); assert(result->java_mirror() != nullptr, "must be"); - add_regenerated_class(result->java_mirror()); + RegeneratedClasses::add_class(InstanceKlass::cast(klass), result); result->add_to_hierarchy(THREAD); diff --git a/src/hotspot/share/cds/lambdaFormInvokers.hpp b/src/hotspot/share/cds/lambdaFormInvokers.hpp index 9463427eeca..7bb5e5932c7 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.hpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -38,9 +38,7 @@ class LambdaFormInvokers : public AllStatic { static GrowableArrayCHeap* _lambdaform_lines; // For storing LF form lines (LF_RESOLVE only) in read only table. static Array*>* _static_archive_invokers; - static GrowableArrayCHeap* _regenerated_mirrors; static void regenerate_class(char* name, ClassFileStream& st, TRAPS); - static void add_regenerated_class(oop regenerated_class); public: static void append(char* line); static void dump_static_archive_invokers(); diff --git a/src/hotspot/share/cds/regeneratedClasses.cpp b/src/hotspot/share/cds/regeneratedClasses.cpp new file mode 100644 index 00000000000..6c7be18edd9 --- /dev/null +++ b/src/hotspot/share/cds/regeneratedClasses.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "cds/archiveBuilder.hpp" +#include "cds/regeneratedClasses.hpp" +#include "memory/universe.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/method.hpp" +#include "oops/oopHandle.inline.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/thread.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/resourceHash.hpp" + +using RegeneratedObjTable = ResourceHashtable; +static RegeneratedObjTable* _renegerated_objs = nullptr; // InstanceKlass* and Method* +static GrowableArrayCHeap* _regenerated_mirrors = nullptr; + +// The regenerated Klass is not added to any class loader, so we need +// to keep its java_mirror alive to avoid class unloading. +void RegeneratedClasses::add_class(InstanceKlass* orig_klass, InstanceKlass* regen_klass) { + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); + if (_regenerated_mirrors == nullptr) { + _regenerated_mirrors = new GrowableArrayCHeap(150); + } + _regenerated_mirrors->append(OopHandle(Universe::vm_global(), regen_klass->java_mirror())); + + if (_renegerated_objs == nullptr) { + _renegerated_objs = new (mtClass)RegeneratedObjTable(); + } + + _renegerated_objs->put((address)orig_klass, (address)regen_klass); + Array* methods = orig_klass->methods(); + for (int i = 0; i < methods->length(); i++) { + Method* orig_m = methods->at(i); + Method* regen_m = regen_klass->find_method(orig_m->name(), orig_m->signature()); + if (regen_m == nullptr) { + ResourceMark rm; + log_warning(cds)("Method in original class is missing from regenerated class: " INTPTR_FORMAT " %s", + p2i(orig_m), orig_m->external_name()); + } else { + _renegerated_objs->put((address)orig_m, (address)regen_m); + } + } +} + +bool RegeneratedClasses::has_been_regenerated(address orig_obj) { + if (_renegerated_objs == nullptr) { + return false; + } else { + return _renegerated_objs->get(orig_obj) != nullptr; + } +} + +void RegeneratedClasses::record_regenerated_objects() { + assert_locked_or_safepoint(DumpTimeTable_lock); + if (_renegerated_objs != nullptr) { + auto doit = [&] (address orig_obj, address regen_obj) { + ArchiveBuilder::current()->record_regenerated_object(orig_obj, regen_obj); + }; + _renegerated_objs->iterate_all(doit); + } +} + +void RegeneratedClasses::cleanup() { + MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag); + if (_regenerated_mirrors != nullptr) { + for (int i = 0; i < _regenerated_mirrors->length(); i++) { + _regenerated_mirrors->at(i).release(Universe::vm_global()); + } + delete _regenerated_mirrors; + _regenerated_mirrors = nullptr; + } + if (_renegerated_objs != nullptr) { + delete _renegerated_objs; + } +} diff --git a/src/hotspot/share/cds/regeneratedClasses.hpp b/src/hotspot/share/cds/regeneratedClasses.hpp new file mode 100644 index 00000000000..a1edaffe529 --- /dev/null +++ b/src/hotspot/share/cds/regeneratedClasses.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_CDS_REGENERATEDCLASSES_HPP +#define SHARE_CDS_REGENERATEDCLASSES_HPP + +#include "memory/allStatic.hpp" +#include "utilities/globalDefinitions.hpp" + +class InstanceKlass; + +// CDS regenerates some of the classes that are loaded normally during the dumping +// process. For example, LambdaFormInvokers creates new versions of the four +// java.lang.invoke.xxx$Holder classes that have additional methods. +// +// RegeneratedClasses records the relocation between the "original" and +// "regenerated" versions of these classes. When writing the CDS archive, all +// references to the "original" versions are redirected to the "regenerated" +// versions. +class RegeneratedClasses : public AllStatic { + public: + static void add_class(InstanceKlass* orig_klass, InstanceKlass* regen_klass); + static void cleanup(); + static bool has_been_regenerated(address orig_obj); + static void record_regenerated_objects(); +}; + +#endif // SHARE_CDS_REGENERATEDCLASSES_HPP