7032407: Crash in LinkResolver::runtime_resolve_virtual_method()
Summary: Make CDS reorder vtables so that dump time vtables match run time order, so when redefine classes reinitializes them, they aren't in the wrong order.
Reviewed-by: dcubed, acorn
--- a/hotspot/src/share/vm/classfile/classFileParser.cpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Fri Apr 15 09:36:28 2011 -0400
@@ -2196,11 +2196,12 @@
TRAPS) {
typeArrayHandle nullHandle;
int length = methods()->length();
- // If JVMTI original method ordering is enabled we have to
+ // If JVMTI original method ordering or sharing is enabled we have to
// remember the original class file ordering.
// We temporarily use the vtable_index field in the methodOop to store the
// class file index, so we can read in after calling qsort.
- if (JvmtiExport::can_maintain_original_method_order()) {
+ // Put the method ordering in the shared archive.
+ if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) {
for (int index = 0; index < length; index++) {
methodOop m = methodOop(methods->obj_at(index));
assert(!m->valid_vtable_index(), "vtable index should not be set");
@@ -2214,8 +2215,9 @@
methods_parameter_annotations(),
methods_default_annotations());
- // If JVMTI original method ordering is enabled construct int array remembering the original ordering
- if (JvmtiExport::can_maintain_original_method_order()) {
+ // If JVMTI original method ordering or sharing is enabled construct int
+ // array remembering the original ordering
+ if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) {
typeArrayOop new_ordering = oopFactory::new_permanent_intArray(length, CHECK_(nullHandle));
typeArrayHandle method_ordering(THREAD, new_ordering);
for (int index = 0; index < length; index++) {
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Fri Apr 15 09:36:28 2011 -0400
@@ -1255,6 +1255,16 @@
methodHandle m(THREAD, methodOop(methods->obj_at(index2)));
m()->link_method(m, CHECK_(nh));
}
+ if (JvmtiExport::has_redefined_a_class()) {
+ // Reinitialize vtable because RedefineClasses may have changed some
+ // entries in this vtable for super classes so the CDS vtable might
+ // point to old or obsolete entries. RedefineClasses doesn't fix up
+ // vtables in the shared system dictionary, only the main one.
+ // It also redefines the itable too so fix that too.
+ ResourceMark rm(THREAD);
+ ik->vtable()->initialize_vtable(false, CHECK_(nh));
+ ik->itable()->initialize_itable(false, CHECK_(nh));
+ }
}
if (TraceClassLoading) {
--- a/hotspot/src/share/vm/memory/dump.cpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/memory/dump.cpp Fri Apr 15 09:36:28 2011 -0400
@@ -623,24 +623,48 @@
}
};
-// Itable indices are calculated based on methods array order
-// (see klassItable::compute_itable_index()). Must reinitialize
+// Vtable and Itable indices are calculated based on methods array
+// order (see klassItable::compute_itable_index()). Must reinitialize
// after ALL methods of ALL classes have been reordered.
// We assume that since checkconstraints is false, this method
// cannot throw an exception. An exception here would be
// problematic since this is the VMThread, not a JavaThread.
-class ReinitializeItables: public ObjectClosure {
+class ReinitializeTables: public ObjectClosure {
private:
Thread* _thread;
public:
- ReinitializeItables(Thread* thread) : _thread(thread) {}
+ ReinitializeTables(Thread* thread) : _thread(thread) {}
+
+ // Initialize super vtable first, check if already initialized to avoid
+ // quadradic behavior. The vtable is cleared in remove_unshareable_info.
+ void reinitialize_vtables(klassOop k) {
+ if (k->blueprint()->oop_is_instanceKlass()) {
+ instanceKlass* ik = instanceKlass::cast(k);
+ if (ik->vtable()->is_initialized()) return;
+ if (ik->super() != NULL) {
+ reinitialize_vtables(ik->super());
+ }
+ ik->vtable()->initialize_vtable(false, _thread);
+ }
+ }
void do_object(oop obj) {
if (obj->blueprint()->oop_is_instanceKlass()) {
instanceKlass* ik = instanceKlass::cast((klassOop)obj);
+ ResourceMark rm(_thread);
ik->itable()->initialize_itable(false, _thread);
+ reinitialize_vtables((klassOop)obj);
+#ifdef ASSERT
+ ik->vtable()->verify(tty, true);
+#endif // ASSERT
+ } else if (obj->blueprint()->oop_is_arrayKlass()) {
+ // The vtable for array klasses are that of its super class,
+ // ie. java.lang.Object.
+ arrayKlass* ak = arrayKlass::cast((klassOop)obj);
+ if (ak->vtable()->is_initialized()) return;
+ ak->vtable()->initialize_vtable(false, _thread);
}
}
};
@@ -1205,9 +1229,9 @@
gen->ro_space()->object_iterate(&sort);
gen->rw_space()->object_iterate(&sort);
- ReinitializeItables reinit_itables(THREAD);
- gen->ro_space()->object_iterate(&reinit_itables);
- gen->rw_space()->object_iterate(&reinit_itables);
+ ReinitializeTables reinit_tables(THREAD);
+ gen->ro_space()->object_iterate(&reinit_tables);
+ gen->rw_space()->object_iterate(&reinit_tables);
tty->print_cr("done. ");
tty->cr();
--- a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp Fri Apr 15 09:36:28 2011 -0400
@@ -690,7 +690,8 @@
guarantee(method_ordering->is_perm(), "should be in permspace");
guarantee(method_ordering->is_typeArray(), "should be type array");
int length = method_ordering->length();
- if (JvmtiExport::can_maintain_original_method_order()) {
+ if (JvmtiExport::can_maintain_original_method_order() ||
+ (UseSharedSpaces && length != 0)) {
guarantee(length == methods->length(), "invalid method ordering length");
jlong sum = 0;
for (j = 0; j < length; j++) {
--- a/hotspot/src/share/vm/oops/klass.cpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/oops/klass.cpp Fri Apr 15 09:36:28 2011 -0400
@@ -453,6 +453,14 @@
ik->unlink_class();
}
}
+ // Clear the Java vtable if the oop has one.
+ // The vtable isn't shareable because it's in the wrong order wrt the methods
+ // once the method names get moved and resorted.
+ klassVtable* vt = vtable();
+ if (vt != NULL) {
+ assert(oop_is_instance() || oop_is_array(), "nothing else has vtable");
+ vt->clear_vtable();
+ }
set_subklass(NULL);
set_next_sibling(NULL);
}
--- a/hotspot/src/share/vm/oops/klassVtable.cpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/oops/klassVtable.cpp Fri Apr 15 09:36:28 2011 -0400
@@ -645,6 +645,15 @@
}
}
+// CDS/RedefineClasses support - clear vtables so they can be reinitialized
+void klassVtable::clear_vtable() {
+ for (int i = 0; i < _length; i++) table()[i].clear();
+}
+
+bool klassVtable::is_initialized() {
+ return _length == 0 || table()[0].method() != NULL;
+}
+
// Garbage collection
void klassVtable::oop_follow_contents() {
--- a/hotspot/src/share/vm/oops/klassVtable.hpp Thu Apr 14 23:06:33 2011 -0400
+++ b/hotspot/src/share/vm/oops/klassVtable.hpp Fri Apr 15 09:36:28 2011 -0400
@@ -75,7 +75,15 @@
void initialize_vtable(bool checkconstraints, TRAPS); // initialize vtable of a new klass
- // conputes vtable length (in words) and the number of miranda methods
+ // CDS/RedefineClasses support - clear vtables so they can be reinitialized
+ // at dump time. Clearing gives us an easy way to tell if the vtable has
+ // already been reinitialized at dump time (see dump.cpp). Vtables can
+ // be initialized at run time by RedefineClasses so dumping the right order
+ // is necessary.
+ void clear_vtable();
+ bool is_initialized();
+
+ // computes vtable length (in words) and the number of miranda methods
static void compute_vtable_size_and_num_mirandas(int &vtable_length, int &num_miranda_methods,
klassOop super, objArrayOop methods,
AccessFlags class_flags, Handle classloader,