Skip to content

Commit

Permalink
8308463: Refactor regenerated class handling in lambdaFormInvokers.cpp
Browse files Browse the repository at this point in the history
Reviewed-by: coleenp, dholmes, matsaave
  • Loading branch information
iklam committed Jun 29, 2023
1 parent 6f58ab2 commit cf8d706
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 28 deletions.
31 changes: 29 additions & 2 deletions src/hotspot/share/cds/archiveBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -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()) {
Expand All @@ -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");
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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");
Expand All @@ -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();
}
Expand Down
10 changes: 10 additions & 0 deletions src/hotspot/share/cds/archiveBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ class ArchiveBuilder : public StackObj {
}
}

// 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");
Expand Down Expand Up @@ -368,6 +377,7 @@ class ArchiveBuilder : public StackObj {
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);
Expand Down
5 changes: 2 additions & 3 deletions src/hotspot/share/cds/dynamicArchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -346,7 +345,7 @@ class VM_PopulateDynamicDumpSharedSpace: public VM_GC_Sync_Operation {
_builder.doit();
}
~VM_PopulateDynamicDumpSharedSpace() {
LambdaFormInvokers::cleanup_regenerated_classes();
RegeneratedClasses::cleanup();
}
};

Expand Down
22 changes: 2 additions & 20 deletions src/hotspot/share/cds/lambdaFormInvokers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -51,7 +52,6 @@

GrowableArrayCHeap<char*, mtClassShared>* LambdaFormInvokers::_lambdaform_lines = nullptr;
Array<Array<char>*>* LambdaFormInvokers::_static_archive_invokers = nullptr;
GrowableArrayCHeap<OopHandle, mtClassShared>* LambdaFormInvokers::_regenerated_mirrors = nullptr;

#define NUM_FILTER 4
static const char* filter[NUM_FILTER] = {"java.lang.invoke.Invokers$Holder",
Expand All @@ -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<OopHandle, mtClassShared>(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 {
Expand Down Expand Up @@ -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);

Expand Down
4 changes: 1 addition & 3 deletions src/hotspot/share/cds/lambdaFormInvokers.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -38,9 +38,7 @@ class LambdaFormInvokers : public AllStatic {
static GrowableArrayCHeap<char*, mtClassShared>* _lambdaform_lines;
// For storing LF form lines (LF_RESOLVE only) in read only table.
static Array<Array<char>*>* _static_archive_invokers;
static GrowableArrayCHeap<OopHandle, mtClassShared>* _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();
Expand Down
99 changes: 99 additions & 0 deletions src/hotspot/share/cds/regeneratedClasses.cpp
Original file line number Diff line number Diff line change
@@ -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<address, address, 15889, AnyObj::C_HEAP, mtClassShared>;
static RegeneratedObjTable* _renegerated_objs = nullptr; // InstanceKlass* and Method*
static GrowableArrayCHeap<OopHandle, mtClassShared>* _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<OopHandle, mtClassShared>(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<Method*>* 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;
}
}
49 changes: 49 additions & 0 deletions src/hotspot/share/cds/regeneratedClasses.hpp
Original file line number Diff line number Diff line change
@@ -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

1 comment on commit cf8d706

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.