--- 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;
}
--- 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();
--- 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);
--- 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:
--- 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();
};
--- 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.
--- 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; }