From 8b7d184e7f3211a297fb6719264e615256720c0b Mon Sep 17 00:00:00 2001 From: jiangli Date: Wed, 27 Apr 2016 14:41:55 -0400 Subject: [PATCH] 8153399: Constrain AppCDS behavior (back port) Reviewed-by: iklam, acorn, mschoene --- src/share/vm/memory/metaspaceShared.cpp | 2 + src/share/vm/memory/metaspaceShared.hpp | 7 +- src/share/vm/oops/instanceKlass.cpp | 7 +- src/share/vm/oops/klassVtable.cpp | 102 +++++++++++++++++++----- src/share/vm/oops/klassVtable.hpp | 13 +++ src/share/vm/oops/method.cpp | 28 +++++++ src/share/vm/oops/method.hpp | 4 +- 7 files changed, 138 insertions(+), 25 deletions(-) diff --git a/src/share/vm/memory/metaspaceShared.cpp b/src/share/vm/memory/metaspaceShared.cpp index d1102c466..d5826d6e0 100644 --- a/src/share/vm/memory/metaspaceShared.cpp +++ b/src/share/vm/memory/metaspaceShared.cpp @@ -54,6 +54,7 @@ bool MetaspaceShared::_link_classes_made_progress; bool MetaspaceShared::_check_classes_made_progress; bool MetaspaceShared::_has_error_classes; bool MetaspaceShared::_archive_loading_failed = false; +bool MetaspaceShared::_remapped_readwrite = false; // Read/write a data stream for restoring/preserving metadata pointers and // miscellaneous data from/to the shared archive file. @@ -1101,6 +1102,7 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() { if (!mapinfo->remap_shared_readonly_as_readwrite()) { return false; } + _remapped_readwrite = true; } return true; } diff --git a/src/share/vm/memory/metaspaceShared.hpp b/src/share/vm/memory/metaspaceShared.hpp index 837625b7d..2f3abae6a 100644 --- a/src/share/vm/memory/metaspaceShared.hpp +++ b/src/share/vm/memory/metaspaceShared.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ class MetaspaceShared : AllStatic { static bool _check_classes_made_progress; static bool _has_error_classes; static bool _archive_loading_failed; + static bool _remapped_readwrite; public: enum { vtbl_list_size = 17, // number of entries in the shared space vtable list. @@ -123,6 +124,10 @@ class MetaspaceShared : AllStatic { // sharing is enabled. Simply returns true if sharing is not enabled // or if the remapping has already been done by a prior call. static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true); + static bool remapped_readwrite() { + CDS_ONLY(return _remapped_readwrite); + NOT_CDS(return false); + } static void print_shared_spaces(); diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp index 571bd5a60..481742db9 100644 --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -726,7 +726,12 @@ bool InstanceKlass::link_class_impl( // methods have been rewritten since rewrite may // fabricate new Method*s. // also does loader constraint checking - if (!this_oop()->is_shared()) { + // + // Initialize_vtable and initialize_itable need to be rerun for + // a shared class if the class is not loaded by the NULL classloader. + ClassLoaderData * loader_data = this_oop->class_loader_data(); + if (!(this_oop()->is_shared() && + loader_data->is_the_null_class_loader_data())) { ResourceMark rm(THREAD); this_oop->vtable()->initialize_vtable(true, CHECK_false); this_oop->itable()->initialize_itable(true, CHECK_false); diff --git a/src/share/vm/oops/klassVtable.cpp b/src/share/vm/oops/klassVtable.cpp index f7adf47b9..639ce011f 100644 --- a/src/share/vm/oops/klassVtable.cpp +++ b/src/share/vm/oops/klassVtable.cpp @@ -27,6 +27,7 @@ #include "classfile/vmSymbols.hpp" #include "gc_implementation/shared/markSweep.inline.hpp" #include "memory/gcLocker.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.inline.hpp" #include "oops/instanceKlass.hpp" @@ -47,6 +48,10 @@ inline InstanceKlass* klassVtable::ik() const { return (InstanceKlass*)k; } +bool klassVtable::is_preinitialized_vtable() { + return _klass->is_shared() && !MetaspaceShared::remapped_readwrite(); +} + // this function computes the vtable size (including the size needed for miranda // methods) and the number of miranda methods in this class. @@ -128,6 +133,12 @@ int klassVtable::index_of(Method* m, int len) const { int klassVtable::initialize_from_super(KlassHandle super) { if (super.is_null()) { return 0; + } else if (is_preinitialized_vtable()) { + // A shared class' vtable is preinitialized at dump time. No need to copy + // methods from super class for shared class, as that was already done + // during archiving time. However, if Jvmti has redefined a class, + // copy super class's vtable in case the super class has changed. + return super->vtable()->length(); } else { // copy methods from superKlass // can't inherit from array class, so must be InstanceKlass @@ -157,6 +168,8 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { KlassHandle super (THREAD, klass()->java_super()); int nofNewEntries = 0; + bool is_shared = _klass->is_shared(); + if (PrintVtables && !klass()->oop_is_array()) { ResourceMark rm(THREAD); tty->print_cr("Initializing: %s", _klass->name()->as_C_string()); @@ -169,6 +182,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { #endif if (Universe::is_bootstrapping()) { + assert(!is_shared, "sanity"); // just clear everything for (int i = 0; i < _length; i++) table()[i].clear(); return; @@ -208,6 +222,7 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { if (len > 0) { Array* def_vtable_indices = NULL; if ((def_vtable_indices = ik()->default_vtable_indices()) == NULL) { + assert(!is_shared, "shared class def_vtable_indices does not exist"); def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK); } else { assert(def_vtable_indices->length() == len, "reinit vtable len?"); @@ -222,7 +237,15 @@ void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { // needs new entry if (needs_new_entry) { put_method_at(mh(), initialized); - def_vtable_indices->at_put(i, initialized); //set vtable index + if (is_preinitialized_vtable()) { + // At runtime initialize_vtable is rerun for a shared class + // (loaded by the non-boot loader) as part of link_class_impl(). + // The dumptime vtable index should be the same as the runtime index. + assert(def_vtable_indices->at(i) == initialized, + "dump time vtable index is different from runtime index"); + } else { + def_vtable_indices->at_put(i, initialized); //set vtable index + } initialized++; } } @@ -365,7 +388,8 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar } // we need a new entry if there is no superclass - if (klass->super() == NULL) { + Klass* super = klass->super(); + if (super == NULL) { return allocate_new; } @@ -394,7 +418,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar Symbol* target_classname = target_klass->name(); for(int i = 0; i < super_vtable_len; i++) { - Method* super_method = method_at(i); + Method* super_method; + if (is_preinitialized_vtable()) { + // If this is a shared class, the vtable is already in the final state (fully + // initialized). Need to look at the super's vtable. + klassVtable* superVtable = super->vtable(); + super_method = superVtable->method_at(i); + } else { + super_method = method_at(i); + } // Check if method name matches if (super_method->name() == name && super_method->signature() == signature) { @@ -458,7 +490,15 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar target_method()->set_vtable_index(i); } else { if (def_vtable_indices != NULL) { - def_vtable_indices->at_put(default_index, i); + if (is_preinitialized_vtable()) { + // At runtime initialize_vtable is rerun as part of link_class_impl() + // for a shared class loaded by the non-boot loader. + // The dumptime vtable index should be the same as the runtime index. + assert(def_vtable_indices->at(default_index) == i, + "dump time vtable index is different from runtime index"); + } else { + def_vtable_indices->at_put(default_index, i); + } } assert(super_method->is_default_method() || super_method->is_overpass() || super_method->is_abstract(), "default override error"); @@ -523,24 +563,33 @@ bool klassVtable::update_inherited_vtable(InstanceKlass* klass, methodHandle tar } void klassVtable::put_method_at(Method* m, int index) { + if (is_preinitialized_vtable()) { + // At runtime initialize_vtable is rerun as part of link_class_impl() + // for shared class loaded by the non-boot loader to obtain the loader + // constraints based on the runtime classloaders' context. The dumptime + // method at the vtable index should be the same as the runtime method. + assert(table()[index].method() == m, + "archived method is different from the runtime method"); + } else { #ifndef PRODUCT - if (PrintVtables && Verbose) { - ResourceMark rm; - const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : ""; - tty->print("adding %s at index %d, flags: ", sig, index); - if (m != NULL) { - m->access_flags().print_on(tty); - if (m->is_default_method()) { - tty->print("default "); - } - if (m->is_overpass()) { - tty->print("overpass"); + if (PrintVtables && Verbose) { + ResourceMark rm; + const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : ""; + tty->print("adding %s at index %d, flags: ", sig, index); + if (m != NULL) { + m->access_flags().print_on(tty); + if (m->is_default_method()) { + tty->print("default "); + } + if (m->is_overpass()) { + tty->print("overpass"); + } } + tty->cr(); } - tty->cr(); - } #endif - table()[index].set(m); + table()[index].set(m); + } } // Find out if a method "m" with superclass "super", loader "classloader" and @@ -971,7 +1020,15 @@ bool klassVtable::is_initialized() { void itableMethodEntry::initialize(Method* m) { if (m == NULL) return; - _method = m; + if (MetaspaceShared::is_in_shared_space((void*)&_method) && + !MetaspaceShared::remapped_readwrite()) { + // 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. + assert(_method == m, "sanity"); + } else { + _method = m; + } } klassItable::klassItable(instanceKlassHandle klass) { @@ -1081,7 +1138,11 @@ int klassItable::assign_itable_indices_for_interface(Klass* klass) { tty->cr(); } if (!m->has_vtable_index()) { - assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable"); + // A shared method could have an initialized itable_index that + // is < 0. + assert(m->vtable_index() == Method::pending_itable_index || + m->is_shared(), + "set by initialize_vtable"); m->set_itable_index(ime_num); // Progress to next itable entry ime_num++; @@ -1277,7 +1338,6 @@ void klassItable::dump_itable() { } #endif // INCLUDE_JVMTI - // Setup class InterfaceVisiterClosure : public StackObj { public: diff --git a/src/share/vm/oops/klassVtable.hpp b/src/share/vm/oops/klassVtable.hpp index e4b4c9f51..244f3c0cc 100644 --- a/src/share/vm/oops/klassVtable.hpp +++ b/src/share/vm/oops/klassVtable.hpp @@ -142,6 +142,19 @@ class klassVtable : public ResourceObj { Array* local_interfaces); void verify_against(outputStream* st, klassVtable* vt, int index); inline InstanceKlass* ik() const; + // When loading a class from CDS archive at run time, and no class redefintion + // has happened, it is expected that the class's itable/vtables are + // laid out exactly the same way as they had been during dump time. + // Therefore, in klassVtable::initialize_[iv]table, we do not layout the + // tables again. Instead, we only rerun the process to create/check + // the class loader constraints. In non-product builds, we add asserts to + // guarantee that the table's layout would be the same as at dump time. + // + // If JVMTI redefines any class, the read-only shared memory are remapped + // as read-write. A shared class' vtable/itable are re-initialized and + // might have different layout due to class redefinition of the shared class + // or its super types. + bool is_preinitialized_vtable(); }; diff --git a/src/share/vm/oops/method.cpp b/src/share/vm/oops/method.cpp index ffc9e9cd0..172c61283 100644 --- a/src/share/vm/oops/method.cpp +++ b/src/share/vm/oops/method.cpp @@ -36,6 +36,7 @@ #include "memory/generation.hpp" #include "memory/heapInspection.hpp" #include "memory/metadataFactory.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/oopFactory.hpp" #include "oops/constMethod.hpp" #include "oops/methodData.hpp" @@ -305,6 +306,33 @@ void Method::remove_unshareable_info() { unlink_method(); } +void Method::set_vtable_index(int index) { + if (is_shared() && !MetaspaceShared::remapped_readwrite()) { + // 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. + return; // don't write into the shared class + } else { + _vtable_index = index; + } +} + +void Method::set_itable_index(int index) { + if (is_shared() && !MetaspaceShared::remapped_readwrite()) { + // 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 + // itable index should be the same as the runtime index. + assert(_vtable_index == itable_index_max - index, + "archived itable index is different from runtime index"); + return; // don’t write into the shared class + } else { + _vtable_index = itable_index_max - index; + } + assert(valid_itable_index(), ""); +} + + bool Method::was_executed_more_than(int n) { // Invocation counter is reset when the Method* is compiled. diff --git a/src/share/vm/oops/method.hpp b/src/share/vm/oops/method.hpp index 56bd46a27..212603363 100644 --- a/src/share/vm/oops/method.hpp +++ b/src/share/vm/oops/method.hpp @@ -471,12 +471,12 @@ class Method : public Metadata { DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; }) bool has_vtable_index() const { return _vtable_index >= 0; } int vtable_index() const { return _vtable_index; } - void set_vtable_index(int index) { _vtable_index = index; } + void set_vtable_index(int index); DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; }) bool has_itable_index() const { return _vtable_index <= itable_index_max; } int itable_index() const { assert(valid_itable_index(), ""); return itable_index_max - _vtable_index; } - void set_itable_index(int index) { _vtable_index = itable_index_max - index; assert(valid_itable_index(), ""); } + void set_itable_index(int index); // interpreter entry address interpreter_entry() const { return _i2i_entry; } -- GitLab