Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8261090: Store old classfiles in static CDS archive #856

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -6086,18 +6086,6 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
_minor_version = stream->get_u2_fast();
_major_version = stream->get_u2_fast();

if (DumpSharedSpaces && _major_version < JAVA_6_VERSION) {
ResourceMark rm;
warning("Pre JDK 6 class not supported by CDS: %u.%u %s",
_major_version, _minor_version, _class_name->as_C_string());
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_UnsupportedClassVersionError(),
"Unsupported major.minor version for dump time %u.%u",
_major_version,
_minor_version);
}

// Check version numbers - we check this even with verifier off
verify_class_version(_major_version, _minor_version, _class_name, CHECK);

@@ -259,7 +259,9 @@ bool Verifier::is_eligible_for_verification(InstanceKlass* klass, bool should_ve
// already been rewritten to contain constant pool cache indices,
// which the verifier can't understand.
// Shared classes shouldn't have stackmaps either.
!klass->is_shared() &&
// However, bytecodes for shared old classes can be verified because
// they have not been rewritten.
!(klass->is_shared() && klass->is_rewritten()) &&

// As of the fix for 4486457 we disable verification for all of the
// dynamically-generated bytecodes associated with the 1.4
@@ -566,8 +566,9 @@ void Rewriter::rewrite_bytecodes(TRAPS) {
}

void Rewriter::rewrite(InstanceKlass* klass, TRAPS) {
if (!DumpSharedSpaces) {
assert(!klass->is_shared(), "archive methods must not be rewritten at run time");
if (klass->is_shared()) {
assert(!klass->is_rewritten(), "rewritten shared classes cannot be rewritten again");
assert(!klass->can_be_verified_at_dumptime(), "only shared old classes aren't rewritten");
}
ResourceMark rm(THREAD);
Rewriter rw(klass, klass->constants(), klass->methods(), CHECK);
@@ -554,7 +554,9 @@ static void rewrite_nofast_bytecodes_and_calculate_fingerprints() {
InstanceKlass* ik = InstanceKlass::cast(k);
for (int i = 0; i < ik->methods()->length(); i++) {
Method* m = ik->methods()->at(i);
rewrite_nofast_bytecode(m);
if (ik->can_be_verified_at_dumptime()) {
rewrite_nofast_bytecode(m);
}
Fingerprinter fp(m);
// The side effect of this call sets method's fingerprint field.
fp.fingerprint();
@@ -1590,10 +1592,12 @@ class LinkSharedClassesClosure : public KlassClosure {
// Link the class to cause the bytecodes to be rewritten and the
// cpcache to be created. Class verification is done according
// to -Xverify setting.
_made_progress |= MetaspaceShared::try_link_class(ik, THREAD);
guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class");
if (ik->can_be_verified_at_dumptime()) {
_made_progress |= MetaspaceShared::try_link_class(ik, THREAD);
guarantee(!HAS_PENDING_EXCEPTION, "exception in link_class");

ik->constants()->resolve_class_constants(THREAD);
ik->constants()->resolve_class_constants(THREAD);
}
}
}
};
@@ -1770,7 +1774,7 @@ int MetaspaceShared::preload_classes(const char* class_list_path, TRAPS) {
// Returns true if the class's status has changed
bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) {
assert(DumpSharedSpaces, "should only be called during dumping");
if (ik->init_state() < InstanceKlass::linked) {
if (ik->init_state() < InstanceKlass::linked && ik->can_be_verified_at_dumptime()) {
bool saved = BytecodeVerificationLocal;
if (ik->loader_type() == 0 && ik->class_loader() == NULL) {
// The verification decision is based on BytecodeVerificationRemote
@@ -405,8 +405,11 @@ void ConstMethod::copy_annotations_from(ClassLoaderData* loader_data, ConstMetho

void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) {
log_trace(cds)("Iter(ConstMethod): %p", this);

it->push(&_constants);
if (!method()->method_holder()->is_rewritten()) {
it->push(&_constants, MetaspaceClosure::_writable);
} else {
it->push(&_constants);
}
it->push(&_stackmap_data);
if (has_method_annotations()) {
it->push(method_annotations_addr());
@@ -540,3 +543,22 @@ void ConstMethod::verify_on(outputStream* st) {
int max_gap = align_metadata_size(1)*BytesPerWord;
guarantee(gap >= 0 && gap < max_gap, "invalid method layout");
}

AdapterHandlerEntry* ConstMethod::adapter() {
if (is_shared() && _constants->pool_holder()->verified_at_dump_time()) {
return *_adapter_trampoline;
} else {
return _adapter;
}
}

void ConstMethod::update_adapter_trampoline(AdapterHandlerEntry* adapter) {
assert(is_shared() && _constants->pool_holder()->verified_at_dump_time(), "must be");
*_adapter_trampoline = adapter;
assert(this->adapter() == adapter, "must be");
}

void ConstMethod::set_adapter_entry(AdapterHandlerEntry* adapter) {
assert(!(is_shared() && _constants->pool_holder()->verified_at_dump_time()), "shared methods have fixed adapter_trampoline");
_adapter = adapter;
}
@@ -289,28 +289,14 @@ class ConstMethod : public MetaspaceObj {
bool has_stackmap_table() const { return _stackmap_data != NULL; }

// adapter
void set_adapter_entry(AdapterHandlerEntry* adapter) {
assert(!is_shared(), "shared methods have fixed adapter_trampoline");
_adapter = adapter;
}
void set_adapter_entry(AdapterHandlerEntry* adapter);
void set_adapter_trampoline(AdapterHandlerEntry** trampoline) {
assert(DumpSharedSpaces, "must be");
assert(*trampoline == NULL, "must be NULL during dump time, to be initialized at run time");
_adapter_trampoline = trampoline;
}
void update_adapter_trampoline(AdapterHandlerEntry* adapter) {
assert(is_shared(), "must be");
*_adapter_trampoline = adapter;
assert(this->adapter() == adapter, "must be");
}
AdapterHandlerEntry* adapter() {
if (is_shared()) {
return *_adapter_trampoline;
} else {
return _adapter;
}
}

void update_adapter_trampoline(AdapterHandlerEntry* adapter);
AdapterHandlerEntry* adapter();
void init_fingerprint() {
const uint64_t initval = UCONST64(0x8000000000000000);
_fingerprint = initval;
@@ -326,6 +326,9 @@ void ConstantPool::resolve_class_constants(TRAPS) {

// CDS support. Create a new resolved_references array.
void ConstantPool::restore_unshareable_info(TRAPS) {
if (!_pool_holder->is_linked() && !_pool_holder->is_rewritten()) {
return;
}
assert(is_constantPool(), "ensure C++ vtable is restored");
assert(on_stack(), "should always be set for shared constant pools");
assert(is_shared(), "should always be set for shared constant pools");
@@ -362,6 +365,9 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
}

void ConstantPool::remove_unshareable_info() {
if (!_pool_holder->is_linked() && !_pool_holder->verified_at_dump_time()) {
return;
}
// Resolved references are not in the shared archive.
// Save the length for restoration. It is not necessarily the same length
// as reference_map.length() if invokedynamic is saved. It is needed when
@@ -2240,13 +2240,21 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {

it->push(&_annotations);
it->push((Klass**)&_array_klasses);
it->push(&_constants);
if (!is_rewritten()) {
it->push(&_constants, MetaspaceClosure::_writable);
} else {
it->push(&_constants);
}
it->push(&_inner_classes);
it->push(&_array_name);
#if INCLUDE_JVMTI
it->push(&_previous_versions);
#endif
it->push(&_methods);
if (!is_rewritten()) {
it->push(&_methods, MetaspaceClosure::_writable);
} else {
it->push(&_methods);
}
it->push(&_default_methods);
it->push(&_local_interfaces);
it->push(&_transitive_interfaces);
@@ -2278,6 +2286,10 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
}

void InstanceKlass::remove_unshareable_info() {
if (can_be_verified_at_dumptime()) {
// Remember this so we can avoid walking the hierarchy at runtime.
set_verified_at_dump_time();
}
Klass::remove_unshareable_info();

if (is_in_error_state()) {
@@ -2378,6 +2390,30 @@ void InstanceKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handl
}
}

// Check if a class or any of its supertypes has a version older than 50.
// CDS will not perform verification of old classes during dump time because
// without changing the old verifier, the verification constraint cannot be
// retrieved during dump time.
// Verification of archived old classes will be performed during run time.
bool InstanceKlass::can_be_verified_at_dumptime() const {
if (major_version() < 50 /*JAVA_6_VERSION*/) {
return false;
}
if (java_super() != NULL && !java_super()->can_be_verified_at_dumptime()) {
return false;
}
Array<Klass*>* interfaces = local_interfaces();
int len = interfaces->length();
for (int i = 0; i < len; i++) {
Klass* k = interfaces->at(i);
InstanceKlass* ik = InstanceKlass::cast(k);
if (!ik->can_be_verified_at_dumptime()) {
return false;
}
}
return true;
}

// returns true IFF is_in_error_state() has been changed as a result of this call.
bool InstanceKlass::check_sharing_error_state() {
assert(DumpSharedSpaces, "should only be called during dumping");
@@ -1334,7 +1334,7 @@ class InstanceKlass: public Klass {
virtual void remove_unshareable_info();
virtual void remove_java_mirror();
virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS);

bool can_be_verified_at_dumptime() const;
// jvm support
jint compute_modifier_flags(TRAPS) const;

@@ -177,7 +177,8 @@ class Klass : public Metadata {
u2 _shared_class_flags;
enum {
_has_raw_archived_mirror = 1,
_has_signer_and_not_archived = 1 << 2
_has_signer_and_not_archived = 1 << 2,
_verified_at_dump_time = 8
};
#endif
// The _archived_mirror is set at CDS dump time pointing to the cached mirror
@@ -324,6 +325,13 @@ class Klass : public Metadata {
return (_shared_class_flags & _has_signer_and_not_archived) != 0;
}
#endif // INCLUDE_CDS
void set_verified_at_dump_time() {
CDS_ONLY(_shared_class_flags |= _verified_at_dump_time;)
}
bool verified_at_dump_time() const {
CDS_ONLY(return (_shared_class_flags & _verified_at_dump_time) != 0;)
NOT_CDS(return false;)
}

// Obtain the module or package for this class
virtual ModuleEntry* module() const = 0;
@@ -49,7 +49,7 @@ inline InstanceKlass* klassVtable::ik() const {
}

bool klassVtable::is_preinitialized_vtable() {
return _klass->is_shared() && !MetaspaceShared::remapped_readwrite();
return _klass->is_shared() && !MetaspaceShared::remapped_readwrite() && _klass->verified_at_dump_time();
}


@@ -1052,7 +1052,8 @@ void itableMethodEntry::initialize(Method* m) {

#ifdef ASSERT
if (MetaspaceShared::is_in_shared_metaspace((void*)&_method) &&
!MetaspaceShared::remapped_readwrite()) {
!MetaspaceShared::remapped_readwrite() &&
m->method_holder()->verified_at_dump_time()) {
// At runtime initialize_itable is rerun as part of link_class_impl()
// for a shared class loaded by the non-boot loader.
// The dumptime itable method entry should be the same as the runtime entry.
@@ -315,7 +315,11 @@ Symbol* Method::klass_name() const {
void Method::metaspace_pointers_do(MetaspaceClosure* it) {
log_trace(cds)("Iter(Method): %p", this);

it->push(&_constMethod);
if (!method_holder()->is_rewritten()) {
it->push(&_constMethod, MetaspaceClosure::_writable);
} else {
it->push(&_constMethod);
}
it->push(&_method_data);
it->push(&_method_counters);
}
@@ -330,7 +334,7 @@ void Method::remove_unshareable_info() {
}

void Method::set_vtable_index(int index) {
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
if (is_shared() && !MetaspaceShared::remapped_readwrite() && method_holder()->verified_at_dump_time()) {
// At runtime initialize_vtable is rerun as part of link_class_impl()
// for a shared class loaded by the non-boot loader to obtain the loader
// constraints based on the runtime classloaders' context.
@@ -341,7 +345,7 @@ void Method::set_vtable_index(int index) {
}

void Method::set_itable_index(int index) {
if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
if (is_shared() && !MetaspaceShared::remapped_readwrite() && method_holder()->verified_at_dump_time()) {
// At runtime initialize_itable is rerun as part of link_class_impl()
// for a shared class loaded by the non-boot loader to obtain the loader
// constraints based on the runtime classloaders' context. The dumptime
@@ -982,20 +986,28 @@ void Method::unlink_method() {
assert(DumpSharedSpaces, "dump time only");
// Set the values to what they should be at run time. Note that
// this Method can no longer be executed during dump time.
_i2i_entry = Interpreter::entry_for_cds_method(this);
_from_interpreted_entry = _i2i_entry;
if (method_holder()->verified_at_dump_time()) {
_i2i_entry = Interpreter::entry_for_cds_method(this);
_from_interpreted_entry = _i2i_entry;
} else {
_i2i_entry = NULL;
_from_interpreted_entry = NULL;
}

if (is_native()) {
*native_function_addr() = NULL;
set_signature_handler(NULL);
}
NOT_PRODUCT(set_compiled_invocation_count(0);)

CDSAdapterHandlerEntry* cds_adapter = (CDSAdapterHandlerEntry*)adapter();
constMethod()->set_adapter_trampoline(cds_adapter->get_adapter_trampoline());
_from_compiled_entry = cds_adapter->get_c2i_entry_trampoline();
assert(*((int*)_from_compiled_entry) == 0, "must be NULL during dump time, to be initialized at run time");

if (method_holder()->verified_at_dump_time()) {
CDSAdapterHandlerEntry* cds_adapter = (CDSAdapterHandlerEntry*)adapter();
constMethod()->set_adapter_trampoline(cds_adapter->get_adapter_trampoline());
_from_compiled_entry = cds_adapter->get_c2i_entry_trampoline();
assert(*((int*)_from_compiled_entry) == 0, "must be NULL during dump time, to be initialized at run time");
} else {
_from_interpreted_entry = NULL;
}
set_method_data(NULL);
clear_method_counters();
}
@@ -1077,7 +1089,7 @@ void Method::unlink_method() {
void Method::link_method(const methodHandle& h_method, TRAPS) {
// If the code cache is full, we may reenter this function for the
// leftover methods that weren't linked.
if (is_shared()) {
if (is_shared() && method_holder()->verified_at_dump_time()) {
address entry = Interpreter::entry_for_cds_method(h_method);
assert(entry != NULL && entry == _i2i_entry,
"should be correctly set during dump time");
@@ -1094,7 +1106,7 @@ void Method::link_method(const methodHandle& h_method, TRAPS) {
// Setup interpreter entrypoint
assert(this == h_method(), "wrong h_method()" );

if (!is_shared()) {
if (!(is_shared() && method_holder()->verified_at_dump_time())) {
assert(adapter() == NULL, "init'd to NULL");
address entry = Interpreter::entry_for_method(h_method);
assert(entry != NULL, "interpreter entry must be non-null");
@@ -1138,8 +1150,7 @@ address Method::make_adapters(const methodHandle& mh, TRAPS) {
THROW_MSG_NULL(vmSymbols::java_lang_VirtualMachineError(), "Out of space in CodeCache for adapters");
}
}

if (mh->is_shared()) {
if (mh->is_shared() && mh->method_holder()->verified_at_dump_time()) {
assert(mh->adapter() == adapter, "must be");
assert(mh->_from_compiled_entry != NULL, "must be");
} else {
@@ -1150,8 +1161,11 @@ address Method::make_adapters(const methodHandle& mh, TRAPS) {
}

void Method::restore_unshareable_info(TRAPS) {
assert(is_method() && is_valid_method(this), "ensure C++ vtable is restored");
if (!method_holder()->verified_at_dump_time()) {
return;
}

assert(is_method() && is_valid_method(this), "ensure C++ vtable is restored");
// Since restore_unshareable_info can be called more than once for a method, don't
// redo any work.
if (adapter() == NULL) {