8275731: CDS archived enums objects are recreated at runtime
Reviewed-by: coleenp, ccheung
This commit is contained in:
parent
c7cd1487fe
commit
d983d108c5
src/hotspot/share
cds
cdsHeapVerifier.cppcdsHeapVerifier.hppdumpTimeClassInfo.cppdumpTimeClassInfo.hppheapShared.cppheapShared.hpprunTimeClassInfo.cpprunTimeClassInfo.hpp
classfile
oops
test/hotspot/jtreg/runtime/cds/appcds/cacheObject
305
src/hotspot/share/cds/cdsHeapVerifier.cpp
Normal file
305
src/hotspot/share/cds/cdsHeapVerifier.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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/cdsHeapVerifier.hpp"
|
||||
#include "classfile/classLoaderDataGraph.hpp"
|
||||
#include "classfile/javaClasses.inline.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/fieldStreams.inline.hpp"
|
||||
#include "oops/klass.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
|
||||
// CDSHeapVerifier is used to check for problems where an archived object references a
|
||||
// static field that may be reinitialized at runtime. In the following example,
|
||||
// Foo.get.test()
|
||||
// correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled.
|
||||
//
|
||||
// class Foo {
|
||||
// final Foo archivedFoo; // this field is archived by CDS
|
||||
// Bar bar;
|
||||
// static {
|
||||
// CDS.initializeFromArchive(Foo.class);
|
||||
// if (archivedFoo == null) {
|
||||
// archivedFoo = new Foo();
|
||||
// archivedFoo.bar = Bar.bar;
|
||||
// }
|
||||
// }
|
||||
// static Foo get() { return archivedFoo; }
|
||||
// boolean test() {
|
||||
// return bar == Bar.bar;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// class Bar {
|
||||
// // this field is initialized in both CDS dump time and runtime.
|
||||
// static final Bar bar = new Bar();
|
||||
// }
|
||||
//
|
||||
// The check itself is simple:
|
||||
// [1] CDSHeapVerifier::do_klass() collects all static fields
|
||||
// [2] CDSHeapVerifier::do_entry() checks all the archived objects. None of them
|
||||
// should be in [1]
|
||||
//
|
||||
// However, it's legal for *some* static fields to be references. This leads to the
|
||||
// table of ADD_EXCL below.
|
||||
//
|
||||
// [A] In most of the cases, the module bootstrap code will update the static field
|
||||
// to point to part of the archived module graph. E.g.,
|
||||
// - java/lang/System::bootLayer
|
||||
// - jdk/internal/loader/ClassLoaders::BOOT_LOADER
|
||||
// [B] A final static String that's explicitly initialized inside <clinit>, but
|
||||
// its value is deterministic and is always the same string literal.
|
||||
// [C] A non-final static string that is assigned a string literal during class
|
||||
// initialization; this string is never changed during -Xshare:dump.
|
||||
// [D] Simple caches whose value doesn't matter.
|
||||
// [E] Other cases (see comments in-line below).
|
||||
|
||||
CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
|
||||
{
|
||||
# define ADD_EXCL(...) { static const char* e[] = {__VA_ARGS__, NULL}; add_exclusion(e); }
|
||||
|
||||
// Unfortunately this needs to be manually maintained. If
|
||||
// test/hotspot/jtreg/runtime/cds/appcds/cacheObject/ArchivedEnumTest.java fails,
|
||||
// you might need to fix the core library code, or fix the ADD_EXCL entries below.
|
||||
//
|
||||
// class field type
|
||||
ADD_EXCL("java/lang/ClassLoader", "scl"); // A
|
||||
ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "DONTINLINE_SIG", // B
|
||||
"FORCEINLINE_SIG", // B
|
||||
"HIDDEN_SIG", // B
|
||||
"INJECTEDPROFILE_SIG", // B
|
||||
"LF_COMPILED_SIG"); // B
|
||||
ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A
|
||||
"ALL_UNNAMED_MODULE_SET", // A
|
||||
"EVERYONE_MODULE", // A
|
||||
"EVERYONE_SET"); // A
|
||||
ADD_EXCL("java/lang/System", "bootLayer"); // A
|
||||
ADD_EXCL("java/lang/VersionProps", "VENDOR_URL_BUG", // C
|
||||
"VENDOR_URL_VM_BUG", // C
|
||||
"VENDOR_VERSION"); // C
|
||||
ADD_EXCL("java/net/URL$DefaultFactory", "PREFIX"); // B FIXME: JDK-8276561
|
||||
|
||||
// A dummy object used by HashSet. The value doesn't matter and it's never
|
||||
// tested for equality.
|
||||
ADD_EXCL("java/util/HashSet", "PRESENT"); // E
|
||||
ADD_EXCL("jdk/internal/loader/BuiltinClassLoader", "packageToModule"); // A
|
||||
ADD_EXCL("jdk/internal/loader/ClassLoaders", "BOOT_LOADER", // A
|
||||
"APP_LOADER", // A
|
||||
"PLATFORM_LOADER"); // A
|
||||
ADD_EXCL("jdk/internal/loader/URLClassPath", "JAVA_VERSION"); // B
|
||||
ADD_EXCL("jdk/internal/module/Builder", "cachedVersion"); // D
|
||||
ADD_EXCL("jdk/internal/module/ModuleLoaderMap$Mapper", "APP_CLASSLOADER", // A
|
||||
"APP_LOADER_INDEX", // A
|
||||
"PLATFORM_CLASSLOADER", // A
|
||||
"PLATFORM_LOADER_INDEX"); // A
|
||||
ADD_EXCL("jdk/internal/module/ServicesCatalog", "CLV"); // A
|
||||
|
||||
// This just points to an empty Map
|
||||
ADD_EXCL("jdk/internal/reflect/Reflection", "methodFilterMap"); // E
|
||||
ADD_EXCL("jdk/internal/util/StaticProperty", "FILE_ENCODING"); // C
|
||||
|
||||
// Integer for 0 and 1 are in java/lang/Integer$IntegerCache and are archived
|
||||
ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E
|
||||
"ZERO_INT"); // E
|
||||
ADD_EXCL("sun/security/util/SecurityConstants", "PROVIDER_VER"); // C
|
||||
|
||||
|
||||
# undef ADD_EXCL
|
||||
|
||||
ClassLoaderDataGraph::classes_do(this);
|
||||
}
|
||||
|
||||
CDSHeapVerifier::~CDSHeapVerifier() {
|
||||
if (_problems > 0) {
|
||||
log_warning(cds, heap)("Scanned %d objects. Found %d case(s) where "
|
||||
"an object points to a static field that may be "
|
||||
"reinitialized at runtime.", _archived_objs, _problems);
|
||||
}
|
||||
}
|
||||
|
||||
class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
|
||||
CDSHeapVerifier* _verifier;
|
||||
InstanceKlass* _ik;
|
||||
const char** _exclusions;
|
||||
public:
|
||||
CheckStaticFields(CDSHeapVerifier* verifier, InstanceKlass* ik)
|
||||
: _verifier(verifier), _ik(ik) {
|
||||
_exclusions = _verifier->find_exclusion(_ik);
|
||||
}
|
||||
|
||||
void do_field(fieldDescriptor* fd) {
|
||||
if (fd->field_type() != T_OBJECT) {
|
||||
return;
|
||||
}
|
||||
|
||||
oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
|
||||
if (static_obj_field != NULL) {
|
||||
Klass* klass = static_obj_field->klass();
|
||||
if (_exclusions != NULL) {
|
||||
for (const char** p = _exclusions; *p != NULL; p++) {
|
||||
if (fd->name()->equals(*p)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) {
|
||||
// This field looks like like this in the Java source:
|
||||
// static final SOME_STRING = "a string literal";
|
||||
// This string literal has been stored in the shared string table, so it's OK
|
||||
// for the archived objects to refer to it.
|
||||
return;
|
||||
}
|
||||
if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) {
|
||||
// This field points to an archived mirror.
|
||||
return;
|
||||
}
|
||||
if (klass->has_archived_enum_objs()) {
|
||||
// This klass is a subclass of java.lang.Enum. If any instance of this klass
|
||||
// has been archived, we will archive all static fields of this klass.
|
||||
// See HeapShared::initialize_enum_klass().
|
||||
return;
|
||||
}
|
||||
|
||||
// This field *may* be initialized to a different value at runtime. Remember it
|
||||
// and check later if it appears in the archived object graph.
|
||||
_verifier->add_static_obj_field(_ik, static_obj_field, fd->name());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Remember all the static object fields of every class that are currently
|
||||
// loaded.
|
||||
void CDSHeapVerifier::do_klass(Klass* k) {
|
||||
if (k->is_instance_klass()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
|
||||
if (HeapShared::is_subgraph_root_class(ik)) {
|
||||
// ik is inside one of the ArchivableStaticFieldInfo tables
|
||||
// in heapShared.cpp. We assume such classes are programmed to
|
||||
// update their static fields correctly at runtime.
|
||||
return;
|
||||
}
|
||||
|
||||
CheckStaticFields csf(this, ik);
|
||||
ik->do_local_static_fields(&csf);
|
||||
}
|
||||
}
|
||||
|
||||
void CDSHeapVerifier::add_static_obj_field(InstanceKlass* ik, oop field, Symbol* name) {
|
||||
StaticFieldInfo info = {ik, name};
|
||||
_table.put(field, info);
|
||||
}
|
||||
|
||||
inline bool CDSHeapVerifier::do_entry(oop& orig_obj, HeapShared::CachedOopInfo& value) {
|
||||
_archived_objs++;
|
||||
|
||||
StaticFieldInfo* info = _table.get(orig_obj);
|
||||
if (info != NULL) {
|
||||
ResourceMark rm;
|
||||
LogStream ls(Log(cds, heap)::warning());
|
||||
ls.print_cr("Archive heap points to a static field that may be reinitialized at runtime:");
|
||||
ls.print_cr("Field: %s::%s", info->_holder->name()->as_C_string(), info->_name->as_C_string());
|
||||
ls.print("Value: ");
|
||||
orig_obj->print_on(&ls);
|
||||
ls.print_cr("--- trace begin ---");
|
||||
trace_to_root(orig_obj, NULL, &value);
|
||||
ls.print_cr("--- trace end ---");
|
||||
ls.cr();
|
||||
_problems ++;
|
||||
}
|
||||
|
||||
return true; /* keep on iterating */
|
||||
}
|
||||
|
||||
class CDSHeapVerifier::TraceFields : public FieldClosure {
|
||||
oop _orig_obj;
|
||||
oop _orig_field;
|
||||
LogStream* _ls;
|
||||
|
||||
public:
|
||||
TraceFields(oop orig_obj, oop orig_field, LogStream* ls)
|
||||
: _orig_obj(orig_obj), _orig_field(orig_field), _ls(ls) {}
|
||||
|
||||
void do_field(fieldDescriptor* fd) {
|
||||
if (fd->field_type() == T_OBJECT || fd->field_type() == T_ARRAY) {
|
||||
oop obj_field = _orig_obj->obj_field(fd->offset());
|
||||
if (obj_field == _orig_field) {
|
||||
_ls->print("::%s (offset = %d)", fd->name()->as_C_string(), fd->offset());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Hint: to exercise this function, uncomment out one of the ADD_EXCL lines above.
|
||||
int CDSHeapVerifier::trace_to_root(oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* p) {
|
||||
int level = 0;
|
||||
LogStream ls(Log(cds, heap)::warning());
|
||||
if (p->_referrer != NULL) {
|
||||
HeapShared::CachedOopInfo* ref = HeapShared::archived_object_cache()->get(p->_referrer);
|
||||
assert(ref != NULL, "sanity");
|
||||
level = trace_to_root(p->_referrer, orig_obj, ref) + 1;
|
||||
} else if (java_lang_String::is_instance(orig_obj)) {
|
||||
ls.print_cr("[%2d] (shared string table)", level++);
|
||||
}
|
||||
Klass* k = orig_obj->klass();
|
||||
ResourceMark rm;
|
||||
ls.print("[%2d] ", level);
|
||||
orig_obj->print_address_on(&ls);
|
||||
ls.print(" %s", k->internal_name());
|
||||
if (orig_field != NULL) {
|
||||
if (k->is_instance_klass()) {
|
||||
TraceFields clo(orig_obj, orig_field, &ls);;
|
||||
InstanceKlass::cast(k)->do_nonstatic_fields(&clo);
|
||||
} else {
|
||||
assert(orig_obj->is_objArray(), "must be");
|
||||
objArrayOop array = (objArrayOop)orig_obj;
|
||||
for (int i = 0; i < array->length(); i++) {
|
||||
if (array->obj_at(i) == orig_field) {
|
||||
ls.print(" @[%d]", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ls.cr();
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
void CDSHeapVerifier::verify() {
|
||||
CDSHeapVerifier verf;
|
||||
HeapShared::archived_object_cache()->iterate(&verf);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
89
src/hotspot/share/cds/cdsHeapVerifier.hpp
Normal file
89
src/hotspot/share/cds/cdsHeapVerifier.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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 SHARED_CDS_CDSHEAPVERIFIER_HPP
|
||||
#define SHARED_CDS_CDSHEAPVERIFIER_HPP
|
||||
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "memory/iterator.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
|
||||
class InstanceKlass;
|
||||
class Symbol;
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
|
||||
class CDSHeapVerifier : public KlassClosure {
|
||||
class CheckStaticFields;
|
||||
class TraceFields;
|
||||
|
||||
int _archived_objs;
|
||||
int _problems;
|
||||
|
||||
struct StaticFieldInfo {
|
||||
InstanceKlass* _holder;
|
||||
Symbol* _name;
|
||||
};
|
||||
|
||||
ResourceHashtable<oop, StaticFieldInfo,
|
||||
15889, // prime number
|
||||
ResourceObj::C_HEAP,
|
||||
mtClassShared,
|
||||
HeapShared::oop_hash> _table;
|
||||
|
||||
GrowableArray<const char**> _exclusions;
|
||||
|
||||
void add_exclusion(const char** excl) {
|
||||
_exclusions.append(excl);
|
||||
}
|
||||
void add_static_obj_field(InstanceKlass* ik, oop field, Symbol* name);
|
||||
|
||||
const char** find_exclusion(InstanceKlass* ik) {
|
||||
for (int i = 0; i < _exclusions.length(); i++) {
|
||||
const char** excl = _exclusions.at(i);
|
||||
if (ik->name()->equals(excl[0])) {
|
||||
return &excl[1];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int trace_to_root(oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* p);
|
||||
|
||||
CDSHeapVerifier();
|
||||
~CDSHeapVerifier();
|
||||
|
||||
public:
|
||||
|
||||
// Overrides KlassClosure::do_klass()
|
||||
virtual void do_klass(Klass* k);
|
||||
|
||||
// For ResourceHashtable::iterate()
|
||||
inline bool do_entry(oop& orig_obj, HeapShared::CachedOopInfo& value);
|
||||
|
||||
static void verify() NOT_DEBUG_RETURN;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_CDS_JAVA_HEAP
|
||||
#endif // SHARED_CDS_CDSHEAPVERIFIER_HPP
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, 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
|
||||
@ -25,6 +25,7 @@
|
||||
#include "precompiled.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/dumpTimeClassInfo.inline.hpp"
|
||||
#include "cds/runTimeClassInfo.hpp"
|
||||
#include "classfile/classLoader.hpp"
|
||||
#include "classfile/classLoaderData.inline.hpp"
|
||||
#include "classfile/systemDictionaryShared.hpp"
|
||||
@ -45,6 +46,7 @@ DumpTimeClassInfo DumpTimeClassInfo::clone() {
|
||||
clone._verifier_constraints = NULL;
|
||||
clone._verifier_constraint_flags = NULL;
|
||||
clone._loader_constraints = NULL;
|
||||
clone._enum_klass_static_fields = NULL;
|
||||
int clone_num_verifier_constraints = num_verifier_constraints();
|
||||
if (clone_num_verifier_constraints > 0) {
|
||||
clone._verifier_constraints = new (ResourceObj::C_HEAP, mtClass) GrowableArray<DTVerifierConstraint>(clone_num_verifier_constraints, mtClass);
|
||||
@ -61,9 +63,16 @@ DumpTimeClassInfo DumpTimeClassInfo::clone() {
|
||||
clone._loader_constraints->append(_loader_constraints->at(i));
|
||||
}
|
||||
}
|
||||
assert(_enum_klass_static_fields == NULL, "This should not happen with jcmd VM.cds dumping");
|
||||
return clone;
|
||||
}
|
||||
|
||||
size_t DumpTimeClassInfo::runtime_info_bytesize() const {
|
||||
return RunTimeClassInfo::byte_size(_klass, num_verifier_constraints(),
|
||||
num_loader_constraints(),
|
||||
num_enum_klass_static_fields());
|
||||
}
|
||||
|
||||
void DumpTimeClassInfo::add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
|
||||
if (_verifier_constraints == NULL) {
|
||||
@ -144,6 +153,18 @@ void DumpTimeClassInfo::record_linking_constraint(Symbol* name, Handle loader1,
|
||||
}
|
||||
}
|
||||
|
||||
void DumpTimeClassInfo::add_enum_klass_static_field(int archived_heap_root_index) {
|
||||
if (_enum_klass_static_fields == NULL) {
|
||||
_enum_klass_static_fields = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(20, mtClass);
|
||||
}
|
||||
_enum_klass_static_fields->append(archived_heap_root_index);
|
||||
}
|
||||
|
||||
int DumpTimeClassInfo::enum_klass_static_field(int which_field) {
|
||||
assert(_enum_klass_static_fields != NULL, "must be");
|
||||
return _enum_klass_static_fields->at(which_field);
|
||||
}
|
||||
|
||||
bool DumpTimeClassInfo::is_builtin() {
|
||||
return SystemDictionaryShared::is_builtin(_klass);
|
||||
}
|
||||
|
@ -77,6 +77,7 @@ public:
|
||||
GrowableArray<DTVerifierConstraint>* _verifier_constraints;
|
||||
GrowableArray<char>* _verifier_constraint_flags;
|
||||
GrowableArray<DTLoaderConstraint>* _loader_constraints;
|
||||
GrowableArray<int>* _enum_klass_static_fields;
|
||||
|
||||
DumpTimeClassInfo() {
|
||||
_klass = NULL;
|
||||
@ -92,28 +93,38 @@ public:
|
||||
_verifier_constraints = NULL;
|
||||
_verifier_constraint_flags = NULL;
|
||||
_loader_constraints = NULL;
|
||||
_enum_klass_static_fields = NULL;
|
||||
}
|
||||
|
||||
void add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||||
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
|
||||
void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2);
|
||||
|
||||
void add_enum_klass_static_field(int archived_heap_root_index);
|
||||
int enum_klass_static_field(int which_field);
|
||||
bool is_builtin();
|
||||
|
||||
int num_verifier_constraints() {
|
||||
if (_verifier_constraint_flags != NULL) {
|
||||
return _verifier_constraint_flags->length();
|
||||
} else {
|
||||
private:
|
||||
template <typename T>
|
||||
static int array_length_or_zero(GrowableArray<T>* array) {
|
||||
if (array == NULL) {
|
||||
return 0;
|
||||
} else {
|
||||
return array->length();
|
||||
}
|
||||
}
|
||||
|
||||
int num_loader_constraints() {
|
||||
if (_loader_constraints != NULL) {
|
||||
return _loader_constraints->length();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
public:
|
||||
|
||||
int num_verifier_constraints() const {
|
||||
return array_length_or_zero(_verifier_constraint_flags);
|
||||
}
|
||||
|
||||
int num_loader_constraints() const {
|
||||
return array_length_or_zero(_loader_constraints);
|
||||
}
|
||||
|
||||
int num_enum_klass_static_fields() const {
|
||||
return array_length_or_zero(_enum_klass_static_fields);
|
||||
}
|
||||
|
||||
void metaspace_pointers_do(MetaspaceClosure* it) {
|
||||
@ -151,9 +162,10 @@ public:
|
||||
void set_failed_verification() { _failed_verification = true; }
|
||||
InstanceKlass* nest_host() const { return _nest_host; }
|
||||
void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; }
|
||||
DumpTimeClassInfo clone();
|
||||
};
|
||||
|
||||
DumpTimeClassInfo clone();
|
||||
size_t runtime_info_bytesize() const;
|
||||
};
|
||||
|
||||
inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) {
|
||||
if (DumpSharedSpaces) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "precompiled.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/archiveUtils.hpp"
|
||||
#include "cds/cdsHeapVerifier.hpp"
|
||||
#include "cds/filemap.hpp"
|
||||
#include "cds/heapShared.inline.hpp"
|
||||
#include "cds/metaspaceShared.hpp"
|
||||
@ -42,7 +43,6 @@
|
||||
#include "gc/shared/gcLocker.hpp"
|
||||
#include "gc/shared/gcVMOperations.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logMessage.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/iterator.inline.hpp"
|
||||
#include "memory/metadataFactory.hpp"
|
||||
@ -143,11 +143,24 @@ bool HeapShared::is_archived_object_during_dumptime(oop p) {
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Java heap object archiving support
|
||||
//
|
||||
////////////////////////////////////////////////////////////////
|
||||
static bool is_subgraph_root_class_of(ArchivableStaticFieldInfo fields[], int num, InstanceKlass* ik) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
if (fields[i].klass == ik) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeapShared::is_subgraph_root_class(InstanceKlass* ik) {
|
||||
return is_subgraph_root_class_of(closed_archive_subgraph_entry_fields,
|
||||
num_closed_archive_subgraph_entry_fields, ik) ||
|
||||
is_subgraph_root_class_of(open_archive_subgraph_entry_fields,
|
||||
num_open_archive_subgraph_entry_fields, ik) ||
|
||||
is_subgraph_root_class_of(fmg_open_archive_subgraph_entry_fields,
|
||||
num_fmg_open_archive_subgraph_entry_fields, ik);
|
||||
}
|
||||
|
||||
void HeapShared::fixup_regions() {
|
||||
FileMapInfo* mapinfo = FileMapInfo::current_info();
|
||||
if (is_mapped()) {
|
||||
@ -203,9 +216,9 @@ HeapShared::ArchivedObjectCache* HeapShared::_archived_object_cache = NULL;
|
||||
oop HeapShared::find_archived_heap_object(oop obj) {
|
||||
assert(DumpSharedSpaces, "dump-time only");
|
||||
ArchivedObjectCache* cache = archived_object_cache();
|
||||
oop* p = cache->get(obj);
|
||||
CachedOopInfo* p = cache->get(obj);
|
||||
if (p != NULL) {
|
||||
return *p;
|
||||
return p->_obj;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
@ -302,7 +315,8 @@ oop HeapShared::archive_object(oop obj) {
|
||||
assert(hash_original == hash_archived, "Different hash codes: original %x, archived %x", hash_original, hash_archived);
|
||||
|
||||
ArchivedObjectCache* cache = archived_object_cache();
|
||||
cache->put(obj, archived_oop);
|
||||
CachedOopInfo info = make_cached_oop_info(archived_oop);
|
||||
cache->put(obj, info);
|
||||
if (log_is_enabled(Debug, cds, heap)) {
|
||||
ResourceMark rm;
|
||||
log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT " : %s",
|
||||
@ -336,6 +350,94 @@ void HeapShared::archive_klass_objects() {
|
||||
}
|
||||
}
|
||||
|
||||
// -- Handling of Enum objects
|
||||
// Java Enum classes have synthetic <clinit> methods that look like this
|
||||
// enum MyEnum {FOO, BAR}
|
||||
// MyEnum::<clinint> {
|
||||
// /*static final MyEnum*/ MyEnum::FOO = new MyEnum("FOO");
|
||||
// /*static final MyEnum*/ MyEnum::BAR = new MyEnum("BAR");
|
||||
// }
|
||||
//
|
||||
// If MyEnum::FOO object is referenced by any of the archived subgraphs, we must
|
||||
// ensure the archived value equals (in object address) to the runtime value of
|
||||
// MyEnum::FOO.
|
||||
//
|
||||
// However, since MyEnum::<clinint> is synthetically generated by javac, there's
|
||||
// no way of programatically handling this inside the Java code (as you would handle
|
||||
// ModuleLayer::EMPTY_LAYER, for example).
|
||||
//
|
||||
// Instead, we archive all static field of such Enum classes. At runtime,
|
||||
// HeapShared::initialize_enum_klass() will skip the <clinit> method and pull
|
||||
// the static fields out of the archived heap.
|
||||
void HeapShared::check_enum_obj(int level,
|
||||
KlassSubGraphInfo* subgraph_info,
|
||||
oop orig_obj,
|
||||
bool is_closed_archive) {
|
||||
Klass* k = orig_obj->klass();
|
||||
Klass* relocated_k = ArchiveBuilder::get_relocated_klass(k);
|
||||
if (!k->is_instance_klass()) {
|
||||
return;
|
||||
}
|
||||
InstanceKlass* ik = InstanceKlass::cast(k);
|
||||
if (ik->java_super() == vmClasses::Enum_klass() && !ik->has_archived_enum_objs()) {
|
||||
ResourceMark rm;
|
||||
ik->set_has_archived_enum_objs();
|
||||
relocated_k->set_has_archived_enum_objs();
|
||||
oop mirror = ik->java_mirror();
|
||||
|
||||
for (JavaFieldStream fs(ik); !fs.done(); fs.next()) {
|
||||
if (fs.access_flags().is_static()) {
|
||||
fieldDescriptor& fd = fs.field_descriptor();
|
||||
if (fd.field_type() != T_OBJECT && fd.field_type() != T_ARRAY) {
|
||||
guarantee(false, "static field %s::%s must be T_OBJECT or T_ARRAY",
|
||||
ik->external_name(), fd.name()->as_C_string());
|
||||
}
|
||||
oop oop_field = mirror->obj_field(fd.offset());
|
||||
if (oop_field == NULL) {
|
||||
guarantee(false, "static field %s::%s must not be null",
|
||||
ik->external_name(), fd.name()->as_C_string());
|
||||
} else if (oop_field->klass() != ik && oop_field->klass() != ik->array_klass_or_null()) {
|
||||
guarantee(false, "static field %s::%s is of the wrong type",
|
||||
ik->external_name(), fd.name()->as_C_string());
|
||||
}
|
||||
oop archived_oop_field = archive_reachable_objects_from(level, subgraph_info, oop_field, is_closed_archive);
|
||||
int root_index = append_root(archived_oop_field);
|
||||
log_info(cds, heap)("Archived enum obj @%d %s::%s (" INTPTR_FORMAT " -> " INTPTR_FORMAT ")",
|
||||
root_index, ik->external_name(), fd.name()->as_C_string(),
|
||||
p2i((oopDesc*)oop_field), p2i((oopDesc*)archived_oop_field));
|
||||
SystemDictionaryShared::add_enum_klass_static_field(ik, root_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See comments in HeapShared::check_enum_obj()
|
||||
bool HeapShared::initialize_enum_klass(InstanceKlass* k, TRAPS) {
|
||||
if (!is_fully_available()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RunTimeClassInfo* info = RunTimeClassInfo::get_for(k);
|
||||
assert(info != NULL, "sanity");
|
||||
|
||||
if (log_is_enabled(Info, cds, heap)) {
|
||||
ResourceMark rm;
|
||||
log_info(cds, heap)("Initializing Enum class: %s", k->external_name());
|
||||
}
|
||||
|
||||
oop mirror = k->java_mirror();
|
||||
int i = 0;
|
||||
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
|
||||
if (fs.access_flags().is_static()) {
|
||||
int root_index = info->enum_klass_static_field_root_index_at(i++);
|
||||
fieldDescriptor& fd = fs.field_descriptor();
|
||||
assert(fd.field_type() == T_OBJECT || fd.field_type() == T_ARRAY, "must be");
|
||||
mirror->obj_field_put(fd.offset(), get_root(root_index, /*clear=*/true));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HeapShared::run_full_gc_in_vm_thread() {
|
||||
if (HeapShared::can_write()) {
|
||||
// Avoid fragmentation while archiving heap objects.
|
||||
@ -377,6 +479,7 @@ void HeapShared::archive_objects(GrowableArray<MemRegion>* closed_regions,
|
||||
log_info(cds)("Dumping objects to open archive heap region ...");
|
||||
copy_open_objects(open_regions);
|
||||
|
||||
CDSHeapVerifier::verify();
|
||||
destroy_archived_object_cache();
|
||||
}
|
||||
|
||||
@ -903,6 +1006,11 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
|
||||
KlassSubGraphInfo* _subgraph_info;
|
||||
oop _orig_referencing_obj;
|
||||
oop _archived_referencing_obj;
|
||||
|
||||
// The following are for maintaining a stack for determining
|
||||
// CachedOopInfo::_referrer
|
||||
static WalkOopAndArchiveClosure* _current;
|
||||
WalkOopAndArchiveClosure* _last;
|
||||
public:
|
||||
WalkOopAndArchiveClosure(int level,
|
||||
bool is_closed_archive,
|
||||
@ -912,7 +1020,13 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
|
||||
_level(level), _is_closed_archive(is_closed_archive),
|
||||
_record_klasses_only(record_klasses_only),
|
||||
_subgraph_info(subgraph_info),
|
||||
_orig_referencing_obj(orig), _archived_referencing_obj(archived) {}
|
||||
_orig_referencing_obj(orig), _archived_referencing_obj(archived) {
|
||||
_last = _current;
|
||||
_current = this;
|
||||
}
|
||||
~WalkOopAndArchiveClosure() {
|
||||
_current = _last;
|
||||
}
|
||||
void do_oop(narrowOop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
|
||||
void do_oop( oop *p) { WalkOopAndArchiveClosure::do_oop_work(p); }
|
||||
|
||||
@ -949,8 +1063,26 @@ class WalkOopAndArchiveClosure: public BasicOopIterateClosure {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static WalkOopAndArchiveClosure* current() { return _current; }
|
||||
oop orig_referencing_obj() { return _orig_referencing_obj; }
|
||||
KlassSubGraphInfo* subgraph_info() { return _subgraph_info; }
|
||||
};
|
||||
|
||||
WalkOopAndArchiveClosure* WalkOopAndArchiveClosure::_current = NULL;
|
||||
|
||||
HeapShared::CachedOopInfo HeapShared::make_cached_oop_info(oop orig_obj) {
|
||||
CachedOopInfo info;
|
||||
WalkOopAndArchiveClosure* walker = WalkOopAndArchiveClosure::current();
|
||||
|
||||
info._subgraph_info = (walker == NULL) ? NULL : walker->subgraph_info();
|
||||
info._referrer = (walker == NULL) ? NULL : walker->orig_referencing_obj();
|
||||
info._obj = orig_obj;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void HeapShared::check_closed_region_object(InstanceKlass* k) {
|
||||
// Check fields in the object
|
||||
for (JavaFieldStream fs(k); !fs.done(); fs.next()) {
|
||||
@ -1076,6 +1208,8 @@ oop HeapShared::archive_reachable_objects_from(int level,
|
||||
if (is_closed_archive && orig_k->is_instance_klass()) {
|
||||
check_closed_region_object(InstanceKlass::cast(orig_k));
|
||||
}
|
||||
|
||||
check_enum_obj(level + 1, subgraph_info, orig_obj, is_closed_archive);
|
||||
return archived_obj;
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
class DumpedInternedStrings;
|
||||
class FileMapInfo;
|
||||
class KlassSubGraphInfo;
|
||||
|
||||
struct ArchivableStaticFieldInfo {
|
||||
const char* klass_name;
|
||||
@ -193,7 +194,7 @@ public:
|
||||
static bool is_fully_available() {
|
||||
return is_loaded() || is_mapped();
|
||||
}
|
||||
|
||||
static bool is_subgraph_root_class(InstanceKlass* ik);
|
||||
private:
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
static bool _disable_writing;
|
||||
@ -228,12 +229,23 @@ public:
|
||||
assert(is_in_loaded_heap(o), "must be");
|
||||
}
|
||||
|
||||
struct CachedOopInfo {
|
||||
KlassSubGraphInfo* _subgraph_info;
|
||||
oop _referrer;
|
||||
oop _obj;
|
||||
CachedOopInfo() :_subgraph_info(), _referrer(), _obj() {}
|
||||
};
|
||||
|
||||
private:
|
||||
static void check_enum_obj(int level,
|
||||
KlassSubGraphInfo* subgraph_info,
|
||||
oop orig_obj,
|
||||
bool is_closed_archive);
|
||||
static bool is_in_loaded_heap(uintptr_t o) {
|
||||
return (_loaded_heap_bottom <= o && o < _loaded_heap_top);
|
||||
}
|
||||
|
||||
typedef ResourceHashtable<oop, oop,
|
||||
typedef ResourceHashtable<oop, CachedOopInfo,
|
||||
15889, // prime number
|
||||
ResourceObj::C_HEAP,
|
||||
mtClassShared,
|
||||
@ -272,7 +284,7 @@ private:
|
||||
static RunTimeKlassSubGraphInfoTable _run_time_subgraph_info_table;
|
||||
|
||||
static void check_closed_region_object(InstanceKlass* k);
|
||||
|
||||
static CachedOopInfo make_cached_oop_info(oop orig_obj);
|
||||
static void archive_object_subgraphs(ArchivableStaticFieldInfo fields[],
|
||||
int num,
|
||||
bool is_closed_archive,
|
||||
@ -482,6 +494,7 @@ private:
|
||||
static void init_for_dumping(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static void write_subgraph_info_table() NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static void serialize(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;
|
||||
static bool initialize_enum_klass(InstanceKlass* k, TRAPS) NOT_CDS_JAVA_HEAP_RETURN_(false);
|
||||
};
|
||||
|
||||
#if INCLUDE_CDS_JAVA_HEAP
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, 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
|
||||
@ -64,6 +63,15 @@ void RunTimeClassInfo::init(DumpTimeClassInfo& info) {
|
||||
InstanceKlass* n_h = info.nest_host();
|
||||
set_nest_host(n_h);
|
||||
}
|
||||
if (_klass->has_archived_enum_objs()) {
|
||||
int num = info.num_enum_klass_static_fields();
|
||||
set_num_enum_klass_static_fields(num);
|
||||
for (int i = 0; i < num; i++) {
|
||||
int root_index = info.enum_klass_static_field(i);
|
||||
set_enum_klass_static_field_root_index_at(i, root_index);
|
||||
}
|
||||
}
|
||||
|
||||
ArchivePtrMarker::mark_pointer(&_klass);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, 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
|
||||
@ -64,30 +63,40 @@ public:
|
||||
return (Symbol*)(SharedBaseAddress + _name);
|
||||
}
|
||||
};
|
||||
struct RTEnumKlassStaticFields {
|
||||
int _num;
|
||||
int _root_indices[1];
|
||||
};
|
||||
|
||||
InstanceKlass* _klass;
|
||||
int _num_verifier_constraints;
|
||||
int _num_loader_constraints;
|
||||
|
||||
// optional CrcInfo _crc; (only for UNREGISTERED classes)
|
||||
// optional InstanceKlass* _nest_host
|
||||
// optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints]
|
||||
// optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints]
|
||||
// optional char _verifier_constraint_flags[_num_verifier_constraints]
|
||||
// optional CrcInfo _crc; (only for UNREGISTERED classes)
|
||||
// optional InstanceKlass* _nest_host
|
||||
// optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints]
|
||||
// optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints]
|
||||
// optional char _verifier_constraint_flags[_num_verifier_constraints]
|
||||
// optional RTEnumKlassStaticFields _enum_klass_static_fields;
|
||||
|
||||
private:
|
||||
static size_t header_size_size() {
|
||||
return sizeof(RunTimeClassInfo);
|
||||
return align_up(sizeof(RunTimeClassInfo), wordSize);
|
||||
}
|
||||
static size_t verifier_constraints_size(int num_verifier_constraints) {
|
||||
return sizeof(RTVerifierConstraint) * num_verifier_constraints;
|
||||
return align_up(sizeof(RTVerifierConstraint) * num_verifier_constraints, wordSize);
|
||||
}
|
||||
static size_t verifier_constraint_flags_size(int num_verifier_constraints) {
|
||||
return sizeof(char) * num_verifier_constraints;
|
||||
return align_up(sizeof(char) * num_verifier_constraints, wordSize);
|
||||
}
|
||||
static size_t loader_constraints_size(int num_loader_constraints) {
|
||||
return sizeof(RTLoaderConstraint) * num_loader_constraints;
|
||||
return align_up(sizeof(RTLoaderConstraint) * num_loader_constraints, wordSize);
|
||||
}
|
||||
static size_t enum_klass_static_fields_size(int num_fields) {
|
||||
size_t size = num_fields <= 0 ? 0 : sizeof(RTEnumKlassStaticFields) + (num_fields - 1) * sizeof(int);
|
||||
return align_up(size, wordSize);
|
||||
}
|
||||
|
||||
static size_t nest_host_size(InstanceKlass* klass) {
|
||||
if (klass->is_hidden()) {
|
||||
return sizeof(InstanceKlass*);
|
||||
@ -98,13 +107,15 @@ private:
|
||||
|
||||
static size_t crc_size(InstanceKlass* klass);
|
||||
public:
|
||||
static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) {
|
||||
static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints,
|
||||
int num_enum_klass_static_fields) {
|
||||
return header_size_size() +
|
||||
crc_size(klass) +
|
||||
nest_host_size(klass) +
|
||||
loader_constraints_size(num_loader_constraints) +
|
||||
verifier_constraints_size(num_verifier_constraints) +
|
||||
verifier_constraint_flags_size(num_verifier_constraints);
|
||||
verifier_constraint_flags_size(num_verifier_constraints) +
|
||||
enum_klass_static_fields_size(num_enum_klass_static_fields);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -113,7 +124,7 @@ private:
|
||||
}
|
||||
|
||||
size_t nest_host_offset() const {
|
||||
return crc_offset() + crc_size(_klass);
|
||||
return crc_offset() + crc_size(_klass);
|
||||
}
|
||||
|
||||
size_t loader_constraints_offset() const {
|
||||
@ -125,6 +136,9 @@ private:
|
||||
size_t verifier_constraint_flags_offset() const {
|
||||
return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints);
|
||||
}
|
||||
size_t enum_klass_static_fields_offset() const {
|
||||
return verifier_constraint_flags_offset() + verifier_constraint_flags_size(_num_verifier_constraints);
|
||||
}
|
||||
|
||||
void check_verifier_constraint_offset(int i) const {
|
||||
assert(0 <= i && i < _num_verifier_constraints, "sanity");
|
||||
@ -134,6 +148,11 @@ private:
|
||||
assert(0 <= i && i < _num_loader_constraints, "sanity");
|
||||
}
|
||||
|
||||
RTEnumKlassStaticFields* enum_klass_static_fields_addr() const {
|
||||
assert(_klass->has_archived_enum_objs(), "sanity");
|
||||
return (RTEnumKlassStaticFields*)(address(this) + enum_klass_static_fields_offset());
|
||||
}
|
||||
|
||||
public:
|
||||
CrcInfo* crc() const {
|
||||
assert(crc_size(_klass) > 0, "must be");
|
||||
@ -187,6 +206,23 @@ public:
|
||||
return verifier_constraint_flags()[i];
|
||||
}
|
||||
|
||||
int num_enum_klass_static_fields(int i) const {
|
||||
return enum_klass_static_fields_addr()->_num;
|
||||
}
|
||||
|
||||
void set_num_enum_klass_static_fields(int num) {
|
||||
enum_klass_static_fields_addr()->_num = num;
|
||||
}
|
||||
|
||||
int enum_klass_static_field_root_index_at(int i) const {
|
||||
assert(0 <= i && i < enum_klass_static_fields_addr()->_num, "must be");
|
||||
return enum_klass_static_fields_addr()->_root_indices[i];
|
||||
}
|
||||
|
||||
void set_enum_klass_static_field_root_index_at(int i, int root_index) {
|
||||
assert(0 <= i && i < enum_klass_static_fields_addr()->_num, "must be");
|
||||
enum_klass_static_fields_addr()->_root_indices[i] = root_index;
|
||||
}
|
||||
private:
|
||||
// ArchiveBuilder::make_shallow_copy() has reserved a pointer immediately
|
||||
// before archived InstanceKlasses. We can use this slot to do a quick
|
||||
|
@ -793,6 +793,13 @@ bool SystemDictionaryShared::add_verification_constraint(InstanceKlass* k, Symbo
|
||||
}
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::add_enum_klass_static_field(InstanceKlass* ik, int root_index) {
|
||||
assert(DumpSharedSpaces, "static dump only");
|
||||
DumpTimeClassInfo* info = SystemDictionaryShared::find_or_allocate_info_for_locked(ik);
|
||||
assert(info != NULL, "must be");
|
||||
info->add_enum_klass_static_field(root_index);
|
||||
}
|
||||
|
||||
void SystemDictionaryShared::add_to_dump_time_lambda_proxy_class_dictionary(LambdaProxyClassKey& key,
|
||||
InstanceKlass* proxy_klass) {
|
||||
assert_lock_strong(DumpTimeTable_lock);
|
||||
@ -1174,7 +1181,7 @@ public:
|
||||
|
||||
bool do_entry(InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||
if (!info.is_excluded()) {
|
||||
size_t byte_size = RunTimeClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints());
|
||||
size_t byte_size = info.runtime_info_bytesize();
|
||||
_shared_class_info_size += align_up(byte_size, SharedSpaceObjectAlignment);
|
||||
}
|
||||
return true; // keep on iterating
|
||||
@ -1283,7 +1290,7 @@ public:
|
||||
|
||||
bool do_entry(InstanceKlass* k, DumpTimeClassInfo& info) {
|
||||
if (!info.is_excluded() && info.is_builtin() == _is_builtin) {
|
||||
size_t byte_size = RunTimeClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints());
|
||||
size_t byte_size = info.runtime_info_bytesize();
|
||||
RunTimeClassInfo* record;
|
||||
record = (RunTimeClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size);
|
||||
record->init(info);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2022, 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
|
||||
@ -193,6 +193,9 @@ private:
|
||||
public:
|
||||
static bool is_hidden_lambda_proxy(InstanceKlass* ik);
|
||||
static bool is_early_klass(InstanceKlass* k); // Was k loaded while JvmtiExport::is_early_phase()==true
|
||||
static bool has_archived_enum_objs(InstanceKlass* ik);
|
||||
static void set_has_archived_enum_objs(InstanceKlass* ik);
|
||||
|
||||
static InstanceKlass* find_builtin_class(Symbol* class_name);
|
||||
|
||||
static const RunTimeClassInfo* find_record(RunTimeSharedDictionary* static_dict,
|
||||
@ -243,6 +246,7 @@ public:
|
||||
bool from_is_array, bool from_is_object) NOT_CDS_RETURN_(false);
|
||||
static void check_verification_constraints(InstanceKlass* klass,
|
||||
TRAPS) NOT_CDS_RETURN;
|
||||
static void add_enum_klass_static_field(InstanceKlass* ik, int root_index);
|
||||
static void set_class_has_failed_verification(InstanceKlass* ik) NOT_CDS_RETURN;
|
||||
static bool has_class_failed_verification(InstanceKlass* ik) NOT_CDS_RETURN_(false);
|
||||
static void add_lambda_proxy_class(InstanceKlass* caller_ik,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, 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
|
||||
@ -136,6 +136,7 @@
|
||||
do_klass(ByteArrayInputStream_klass, java_io_ByteArrayInputStream ) \
|
||||
do_klass(URL_klass, java_net_URL ) \
|
||||
do_klass(URLClassLoader_klass, java_net_URLClassLoader ) \
|
||||
do_klass(Enum_klass, java_lang_Enum ) \
|
||||
do_klass(Jar_Manifest_klass, java_util_jar_Manifest ) \
|
||||
do_klass(jdk_internal_loader_BuiltinClassLoader_klass,jdk_internal_loader_BuiltinClassLoader ) \
|
||||
do_klass(jdk_internal_loader_ClassLoaders_klass, jdk_internal_loader_ClassLoaders ) \
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@ -700,6 +700,7 @@
|
||||
template(dumpSharedArchive_signature, "(ZLjava/lang/String;)Ljava/lang/String;") \
|
||||
template(generateLambdaFormHolderClasses, "generateLambdaFormHolderClasses") \
|
||||
template(generateLambdaFormHolderClasses_signature, "([Ljava/lang/String;)[Ljava/lang/Object;") \
|
||||
template(java_lang_Enum, "java/lang/Enum") \
|
||||
template(java_lang_invoke_Invokers_Holder, "java/lang/invoke/Invokers$Holder") \
|
||||
template(java_lang_invoke_DirectMethodHandle_Holder, "java/lang/invoke/DirectMethodHandle$Holder") \
|
||||
template(java_lang_invoke_LambdaForm_Holder, "java/lang/invoke/LambdaForm$Holder") \
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@ -26,6 +26,7 @@
|
||||
#include "jvm.h"
|
||||
#include "cds/archiveUtils.hpp"
|
||||
#include "cds/classListWriter.hpp"
|
||||
#include "cds/heapShared.hpp"
|
||||
#include "cds/metaspaceShared.hpp"
|
||||
#include "classfile/classFileParser.hpp"
|
||||
#include "classfile/classFileStream.hpp"
|
||||
@ -1503,6 +1504,17 @@ void InstanceKlass::call_class_initializer(TRAPS) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if INCLUDE_CDS
|
||||
// This is needed to ensure the consistency of the archived heap objects.
|
||||
if (has_archived_enum_objs()) {
|
||||
assert(is_shared(), "must be");
|
||||
bool initialized = HeapShared::initialize_enum_klass(this, CHECK);
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
methodHandle h_method(THREAD, class_initializer());
|
||||
assert(!is_initialized(), "we cannot initialize twice");
|
||||
LogTarget(Info, class, init) lt;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2022, 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
|
||||
@ -173,10 +173,11 @@ private:
|
||||
// Flags of the current shared class.
|
||||
u2 _shared_class_flags;
|
||||
enum {
|
||||
_archived_lambda_proxy_is_available = 2,
|
||||
_has_value_based_class_annotation = 4,
|
||||
_verified_at_dump_time = 8,
|
||||
_regenerated = 16
|
||||
_archived_lambda_proxy_is_available = 1 << 1,
|
||||
_has_value_based_class_annotation = 1 << 2,
|
||||
_verified_at_dump_time = 1 << 3,
|
||||
_has_archived_enum_objs = 1 << 4,
|
||||
_regenerated = 1 << 5
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -340,6 +341,14 @@ protected:
|
||||
NOT_CDS(return false;)
|
||||
}
|
||||
|
||||
void set_has_archived_enum_objs() {
|
||||
CDS_ONLY(_shared_class_flags |= _has_archived_enum_objs;)
|
||||
}
|
||||
bool has_archived_enum_objs() const {
|
||||
CDS_ONLY(return (_shared_class_flags & _has_archived_enum_objs) != 0;)
|
||||
NOT_CDS(return false;)
|
||||
}
|
||||
|
||||
void set_regenerated() {
|
||||
CDS_ONLY(_shared_class_flags |= _regenerated;)
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor.Requires.Modifier;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ArchivedEnumApp {
|
||||
public static void main(final String[] args) throws Exception {
|
||||
// Validate the archiving of the synthetic Modifier.$VALUES field:
|
||||
for (Modifier mod : Modifier.values()) {
|
||||
check(mod);
|
||||
}
|
||||
if (Modifier.values().length != 4) {
|
||||
throw new RuntimeException("Modifier.$VALUES.length expeced: 4, actual: " + Modifier.values().length);
|
||||
}
|
||||
|
||||
// All 4 enums must exist in synthetic Modifier.$VALUES
|
||||
check_in_array(Modifier.MANDATED);
|
||||
check_in_array(Modifier.STATIC);
|
||||
check_in_array(Modifier.SYNTHETIC);
|
||||
check_in_array(Modifier.TRANSITIVE);
|
||||
|
||||
// Find this module from (archived) boot layer
|
||||
String moduleName = "java.management";
|
||||
Optional<Module> module = ModuleLayer.boot().findModule(moduleName);
|
||||
if (module.isEmpty()) {
|
||||
throw new RuntimeException(moduleName + " module is missing in boot layer");
|
||||
}
|
||||
ModuleDescriptor md = module.get().getDescriptor();
|
||||
System.out.println("Module: " + md);
|
||||
for (Requires r : md.requires()) {
|
||||
System.out.println("Requires: " + r);
|
||||
for (Modifier mod : r.modifiers()) {
|
||||
System.out.println(" modifier: " + mod);
|
||||
check(mod);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Success");
|
||||
}
|
||||
|
||||
static void check(Modifier mod) {
|
||||
// The archived Enum object must equal to one of the following
|
||||
// four values.
|
||||
if (mod != Modifier.MANDATED &&
|
||||
mod != Modifier.STATIC &&
|
||||
mod != Modifier.SYNTHETIC &&
|
||||
mod != Modifier.TRANSITIVE) {
|
||||
|
||||
System.out.println("mod = " + info(mod));
|
||||
System.out.println("Modifier.MANDATED = " + info(Modifier.MANDATED));
|
||||
System.out.println("Modifier.STATIC = " + info(Modifier.STATIC));
|
||||
System.out.println("Modifier.SYNTHETIC = " + info(Modifier.SYNTHETIC));
|
||||
System.out.println("Modifier.TRANSITIVE = " + info(Modifier.TRANSITIVE));
|
||||
|
||||
throw new RuntimeException("Archived enum object does not match static fields in enum class: " + info(mod));
|
||||
}
|
||||
}
|
||||
|
||||
static void check_in_array(Modifier mod) {
|
||||
for (Modifier m : Modifier.values()) {
|
||||
if (mod == m) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Enum object is not in $VALUES array: " + info(mod));
|
||||
}
|
||||
|
||||
static String info(Object o) {
|
||||
return "@0x" + Integer.toHexString(System.identityHashCode(o)) + " " + o;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8275731
|
||||
* @summary Enum objects that are stored in the archived module graph should match
|
||||
* the static final fields in the Enum class.
|
||||
* @modules java.management
|
||||
* @requires vm.cds.write.archived.java.heap
|
||||
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
|
||||
* @build ArchivedEnumApp
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar ArchivedEnumApp.jar ArchivedEnumApp
|
||||
* @run driver ArchivedEnumTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.helpers.ClassFileInstaller;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class ArchivedEnumTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
String appJar = ClassFileInstaller.getJarPath("ArchivedEnumApp.jar");
|
||||
|
||||
OutputAnalyzer out = TestCommon.testDump(appJar,
|
||||
TestCommon.list("ArchivedEnumApp"));
|
||||
// Note: You can get the following line to fail by commenting out
|
||||
// the ADD_EXCL(...) lines in cdsHeapVerifier.cpp
|
||||
out.shouldNotContain("object points to a static field that may be reinitialized at runtime");
|
||||
|
||||
TestCommon.run("-cp", appJar,
|
||||
"-Xlog:cds=debug",
|
||||
"-Xlog:cds+heap",
|
||||
"ArchivedEnumApp").assertNormalExit("Success");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user