# HG changeset patch # User jiangli # Date 1461184777 14400 # Node ID 976b97b59d87750856ba95314ba5dd90caf41fd1 # Parent 29ece76096cb003b51c6633d6da83915bd014143 8153312: Constrain AppCDS behavior Reviewed-by: iklam, acorn, mschoene diff -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/memory/metaspaceShared.cpp --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed Apr 20 16:39:37 2016 -0400 @@ -60,6 +60,7 @@ bool MetaspaceShared::_check_classes_made_progress; bool MetaspaceShared::_has_error_classes; bool MetaspaceShared::_archive_loading_failed = false; +bool MetaspaceShared::_remapped_readwrite = false; address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL; size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0; SharedMiscRegion MetaspaceShared::_mc; @@ -1185,6 +1186,7 @@ if (!mapinfo->remap_shared_readonly_as_readwrite()) { return false; } + _remapped_readwrite = true; } return true; } diff -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/memory/metaspaceShared.hpp --- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Wed Apr 20 16:39:37 2016 -0400 @@ -125,6 +125,7 @@ static bool _check_classes_made_progress; static bool _has_error_classes; static bool _archive_loading_failed; + static bool _remapped_readwrite; static address _cds_i2i_entry_code_buffers; static size_t _cds_i2i_entry_code_buffers_size; @@ -205,6 +206,10 @@ // 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 -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/oops/instanceKlass.cpp --- a/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Apr 20 16:39:37 2016 -0400 @@ -609,7 +609,12 @@ // methods have been rewritten since rewrite may // fabricate new Method*s. // also does loader constraint checking - if (!this_k()->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_k->class_loader_data(); + if (!(this_k->is_shared() && + loader_data->is_the_null_class_loader_data())) { ResourceMark rm(THREAD); this_k->vtable()->initialize_vtable(true, CHECK_false); this_k->itable()->initialize_itable(true, CHECK_false); diff -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/oops/klassVtable.cpp --- a/hotspot/src/share/vm/oops/klassVtable.cpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/oops/klassVtable.cpp Wed Apr 20 16:39:37 2016 -0400 @@ -27,6 +27,7 @@ #include "classfile/vmSymbols.hpp" #include "gc/shared/gcLocker.hpp" #include "logging/log.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.inline.hpp" #include "oops/instanceKlass.hpp" @@ -42,6 +43,10 @@ return InstanceKlass::cast(_klass()); } +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. @@ -126,6 +131,12 @@ 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 klassVtable* superVtable = super->vtable(); @@ -152,6 +163,8 @@ KlassHandle super (THREAD, klass()->java_super()); int nofNewEntries = 0; + bool is_shared = _klass->is_shared(); + if (!klass()->is_array_klass()) { ResourceMark rm(THREAD); log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string()); @@ -164,6 +177,7 @@ #endif if (Universe::is_bootstrapping()) { + assert(!is_shared, "sanity"); // just clear everything for (int i = 0; i < _length; i++) table()[i].clear(); return; @@ -203,6 +217,7 @@ if (len > 0) { Array<int>* 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?"); @@ -217,7 +232,15 @@ // 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++; } } @@ -378,7 +401,8 @@ } // we need a new entry if there is no superclass - if (klass->super() == NULL) { + Klass* super = klass->super(); + if (super == NULL) { return allocate_new; } @@ -407,7 +431,15 @@ 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) { @@ -475,7 +507,15 @@ 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"); @@ -490,17 +530,26 @@ } void klassVtable::put_method_at(Method* m, int index) { - if (log_develop_is_enabled(Trace, vtables)) { - ResourceMark rm; - outputStream* logst = Log(vtables)::trace_stream(); - const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>"; - logst->print("adding %s at index %d, flags: ", sig, index); - if (m != NULL) { - m->print_linkage_flags(logst); + 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 { + if (log_develop_is_enabled(Trace, vtables)) { + ResourceMark rm; + outputStream* logst = Log(vtables)::trace_stream(); + const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>"; + logst->print("adding %s at index %d, flags: ", sig, index); + if (m != NULL) { + m->print_linkage_flags(logst); + } + logst->cr(); } - logst->cr(); + table()[index].set(m); } - table()[index].set(m); } // Find out if a method "m" with superclass "super", loader "classloader" and @@ -950,7 +999,15 @@ 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) { @@ -1054,7 +1111,11 @@ logst->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++; @@ -1248,7 +1309,6 @@ } #endif // INCLUDE_JVMTI - // Setup class InterfaceVisiterClosure : public StackObj { public: diff -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/oops/klassVtable.hpp --- a/hotspot/src/share/vm/oops/klassVtable.hpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/oops/klassVtable.hpp Wed Apr 20 16:39:37 2016 -0400 @@ -153,6 +153,19 @@ Array<Klass*>* 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 -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/oops/method.cpp --- a/hotspot/src/share/vm/oops/method.cpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/oops/method.cpp Wed Apr 20 16:39:37 2016 -0400 @@ -313,6 +313,33 @@ 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 -r 29ece76096cb -r 976b97b59d87 hotspot/src/share/vm/oops/method.hpp --- a/hotspot/src/share/vm/oops/method.hpp Wed Apr 06 21:53:44 2016 -0700 +++ b/hotspot/src/share/vm/oops/method.hpp Wed Apr 20 16:39:37 2016 -0400 @@ -470,12 +470,12 @@ 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; }