JBR-8850 DCEVM: Enable interface replacement

This commit is contained in:
Vladimir Dvorak
2025-05-19 21:47:40 +02:00
parent 8cfd55e764
commit 1d0cbadf05
8 changed files with 528 additions and 131 deletions

View File

@@ -34,17 +34,31 @@
#include "oops/oop.inline.hpp"
#include "utilities/macros.hpp"
#include "utilities/copy.hpp"
#include "runtime/fieldDescriptor.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
DcevmSharedGC* DcevmSharedGC::_static_instance = nullptr;
void DcevmSharedGC::create_static_instance() {
_static_instance = new DcevmSharedGC();
}
void DcevmSharedGC::destroy_static_instance() {
if (_static_instance != nullptr) {
delete _static_instance;
_static_instance = nullptr;
}
}
void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, bool must_be_new) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
copy_rescued_objects_back(rescued_oops, 0, rescued_oops->length(), must_be_new);
}
}
// (DCEVM) Copy the rescued objects to their destination address after compaction.
void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, int from, int to, bool must_be_new) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
for (int i=from; i < to; i++) {
HeapWord* rescued_ptr = rescued_oops->at(i);
oop rescued_obj = cast_to_oop(rescued_ptr);
@@ -52,10 +66,10 @@ void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_
size_t size = rescued_obj->size();
oop new_obj = rescued_obj->forwardee();
assert(!must_be_new || rescued_obj->klass()->new_version() != NULL, "Just checking");
assert(!must_be_new || rescued_obj->klass()->new_version() != nullptr, "Just checking");
Klass* new_klass = rescued_obj->klass()->new_version();
if (new_klass!= NULL) {
if (new_klass->update_information() != NULL) {
if (new_klass!= nullptr) {
if (new_klass->update_information() != nullptr) {
DcevmSharedGC::update_fields(rescued_obj, new_obj);
} else {
rescued_obj->set_klass(new_klass);
@@ -69,11 +83,10 @@ void DcevmSharedGC::copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_
assert(oopDesc::is_oop(new_obj), "must be a valid oop");
}
}
}
void DcevmSharedGC::clear_rescued_objects_resource(GrowableArray<HeapWord*>* rescued_oops) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
for (int i=0; i < rescued_oops->length(); i++) {
HeapWord* rescued_ptr = rescued_oops->at(i);
size_t size = cast_to_oop(rescued_ptr)->size();
@@ -84,7 +97,7 @@ void DcevmSharedGC::clear_rescued_objects_resource(GrowableArray<HeapWord*>* res
}
void DcevmSharedGC::clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued_oops) {
if (rescued_oops != NULL) {
if (rescued_oops != nullptr) {
for (int i=0; i < rescued_oops->length(); i++) {
HeapWord* rescued_ptr = rescued_oops->at(i);
FREE_C_HEAP_ARRAY(HeapWord, rescued_ptr);
@@ -96,7 +109,7 @@ void DcevmSharedGC::clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued
// (DCEVM) Update instances of a class whose fields changed.
void DcevmSharedGC::update_fields(oop q, oop new_location) {
assert(q->klass()->new_version() != NULL, "class of old object must have new version");
assert(q->klass()->new_version() != nullptr, "class of old object must have new version");
Klass* old_klass_oop = q->klass();
Klass* new_klass_oop = q->klass()->new_version();
@@ -107,7 +120,7 @@ void DcevmSharedGC::update_fields(oop q, oop new_location) {
size_t size = q->size_given_klass(old_klass);
size_t new_size = q->size_given_klass(new_klass);
HeapWord* tmp = NULL;
HeapWord* tmp = nullptr;
oop tmp_obj = q;
// Save object somewhere, there is an overlap in fields
@@ -122,39 +135,141 @@ void DcevmSharedGC::update_fields(oop q, oop new_location) {
q->set_klass(new_klass_oop);
int *cur = new_klass_oop->update_information();
assert(cur != NULL, "just checking");
DcevmSharedGC::update_fields(new_location, q, cur);
assert(cur != nullptr, "just checking");
_static_instance->update_fields(new_location, q, cur, false);
if (tmp != NULL) {
if (tmp != nullptr) {
FREE_RESOURCE_ARRAY(HeapWord, tmp, size);
}
}
void DcevmSharedGC::update_fields(oop new_location, oop tmp_obj, int *cur) {
assert(cur != NULL, "just checking");
char* to = (char*)cast_from_oop<HeapWord*>(new_location);
void DcevmSharedGC::update_fields(oop new_obj, oop old_obj, int* cur, bool do_compat_check) {
assert(cur != nullptr, "just checking");
char* to = (char*)cast_from_oop<HeapWord*>(new_obj);
char* src_base = (char *)cast_from_oop<HeapWord*>(old_obj);
while (*cur != 0) {
int size = *cur;
if (size > 0) {
int raw = *cur;
if (raw > 0) {
cur++;
int offset = *cur;
HeapWord* from = (HeapWord*)(((char *)cast_from_oop<HeapWord*>(tmp_obj)) + offset);
if (size == HeapWordSize) {
*((HeapWord*)to) = *from;
} else if (size == HeapWordSize * 2) {
*((HeapWord*)to) = *from;
*(((HeapWord*)to) + 1) = *(from + 1);
int src_offset = *cur;
HeapWord* from = (HeapWord*)(src_base + src_offset);
bool compat_check = do_compat_check && ((raw & UpdateInfoCompatFlag) != 0);
int size = (raw & UpdateInfoLengthMask);
if (!compat_check) {
if (size == HeapWordSize) {
*((HeapWord *) to) = *from;
} else if (size == HeapWordSize * 2) {
*((HeapWord *) to) = *from;
*(((HeapWord *) to) + 1) = *(from + 1);
} else {
Copy::conjoint_jbytes(from, to, size);
}
} else {
Copy::conjoint_jbytes(from, to, size);
assert(size == heapOopSize, "Must be one oop");
int dst_offset = (int)(to - (char*)cast_from_oop<HeapWord*>(new_obj));
oop obj = old_obj->obj_field(src_offset);
if (obj == nullptr) {
new_obj->obj_field_put(dst_offset, nullptr);
} else {
bool compatible = is_compatible(new_obj, dst_offset, obj);
new_obj->obj_field_put(dst_offset, compatible ? obj : (oop)nullptr);
}
}
to += size;
cur++;
} else {
assert(size < 0, "");
int skip = -*cur;
Copy::fill_to_bytes(to, skip, 0);
to += skip;
Copy::fill_to_bytes(to, -raw, 0);
to += -raw;
cur++;
}
}
}
void DcevmSharedGC::update_fields_in_old(oop old_obj, int* cur) {
assert(cur != nullptr, "just checking");
int dst_offset = 0;
while (*cur != 0) {
int raw = *cur;
if (raw > 0) {
cur++;
int size = (raw & UpdateInfoLengthMask);
if ((raw & UpdateInfoCompatFlag) != 0) {
assert(size == heapOopSize, "Must be one oop");
int src_offset = *cur;
oop obj = old_obj->obj_field(src_offset);
if (obj != nullptr) {
bool compatible = is_compatible(old_obj, dst_offset, obj);
if (!compatible) {
old_obj->obj_field_put(src_offset, nullptr);
}
}
}
dst_offset += size;
cur++;
} else {
dst_offset += -raw;
cur++;
}
}
}
static inline bool signature_matches_name(Symbol* sig, Symbol* name) {
const int sig_len = sig->utf8_length();
const int name_len = name->utf8_length();
if (sig_len != name_len + 2) {
return false;
}
const u1* s = sig ->bytes();
const u1* n = name->bytes();
return (s[0] == 'L' && s[sig_len - 1] == ';' && memcmp(s + 1, n, name_len) == 0);
}
bool DcevmSharedGC::is_compatible(oop fld_holder, int fld_offset, oop fld_val) {
assert(oopDesc::is_oop(fld_val), "val has corrupted header");
bool result = false;
Symbol *sig_wanted;
InstanceKlass* holder_ik = InstanceKlass::cast(fld_holder->klass()->newest_version());
Symbol** sig_cached = _field_sig_table->get({holder_ik, fld_offset});
if (sig_cached != nullptr) {
sig_wanted = *sig_cached;
} else {
fieldDescriptor fd_new;
bool ok = holder_ik->find_field_from_offset(fld_offset, false, &fd_new);
assert(ok, "Must exist");
sig_wanted = fd_new.signature();
_field_sig_table->put({holder_ik, fld_offset}, sig_wanted);
}
InstanceKlass *ik = InstanceKlass::cast(fld_val->klass()->newest_version());
bool* hit = _compat_table->get({ik, sig_wanted });
if (hit != nullptr) {
result = *hit;
} else {
InstanceKlass* scan = ik;
while (scan != nullptr && !result) {
if (signature_matches_name(sig_wanted, scan->name())) {
result = true;
break;
}
Array<InstanceKlass*>* ifaces = scan->local_interfaces();
for (int j = 0; j < ifaces->length(); ++j) {
if (signature_matches_name(sig_wanted, ifaces->at(j)->name())) {
result = true;
break;
}
}
scan = (scan->super() != nullptr) ? InstanceKlass::cast(scan->super()) : nullptr;
}
_compat_table->put({ik, sig_wanted }, result);
}
return result;
}

View File

@@ -33,16 +33,76 @@
#include "runtime/timer.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/stack.hpp"
#include "utilities/resourceHash.hpp"
// Shared GC code used from different GC (Serial, CMS, G1) on enhanced redefinition
class DcevmSharedGC : AllStatic {
public:
class DcevmSharedGC : public CHeapObj<mtInternal> {
private:
struct SymbolKey {
InstanceKlass* ik;
Symbol* dst_sig;
};
static unsigned symbol_hash(const SymbolKey& k) {
return (uintptr_t)k.ik ^ (uintptr_t)k.dst_sig;
}
static bool symbol_eq(const SymbolKey& a, const SymbolKey& b) {
return a.ik == b.ik && a.dst_sig == b.dst_sig;
}
typedef ResourceHashtable<SymbolKey, bool, 512,
AnyObj::C_HEAP, mtInternal,
&symbol_hash, &symbol_eq> CompatTable;
struct OffsetKey {
InstanceKlass* ik;
int offset;
};
static unsigned offset_hash(const OffsetKey& k) {
return uintptr_t(k.ik) ^ k.offset;
}
static bool offset_eq(const OffsetKey& a, const OffsetKey& b) {
return a.ik == b.ik && a.offset == b.offset;
}
typedef ResourceHashtable<OffsetKey, Symbol*, 512,
AnyObj::C_HEAP, mtInternal,
&offset_hash, &offset_eq> FieldSigTable;
CompatTable* _compat_table;
FieldSigTable* _field_sig_table;
static DcevmSharedGC* _static_instance;
public:
// ------------------------------------------------------------------
// update info flags
//
// bit 31 : sign bit (<0 = fill, >0 = copy)
// bit 30 : UpdateInfoCompatFlag copy segment requires per-oop compatibility check
// bits 0-29 : raw byte length of the segment
// ------------------------------------------------------------------
static const int UpdateInfoCompatFlag = 1U << 30;
static const int UpdateInfoLengthMask = ~(1U << 31 | UpdateInfoCompatFlag);
DcevmSharedGC() {
_compat_table = new (mtInternal) CompatTable();
_field_sig_table = new (mtInternal) FieldSigTable();
}
~DcevmSharedGC() {
delete _compat_table;
delete _field_sig_table;
}
static void create_static_instance();
static void destroy_static_instance();
static void copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, bool must_be_new);
static void copy_rescued_objects_back(GrowableArray<HeapWord*>* rescued_oops, int from, int to, bool must_be_new);
static void clear_rescued_objects_resource(GrowableArray<HeapWord*>* rescued_oops);
static void clear_rescued_objects_heap(GrowableArray<HeapWord*>* rescued_oops);
static void update_fields(oop q, oop new_location);
static void update_fields(oop new_location, oop tmp_obj, int *cur);
bool is_compatible(oop fld_holder, int fld_offset, oop fld_val);
void update_fields(oop new_obj, oop old_obj, int *cur, bool do_compat_check);
void update_fields_in_old(oop old_obj, int *cur);
};
#endif

View File

@@ -1787,6 +1787,18 @@ bool InstanceKlass::find_field_from_offset(int offset, bool is_static, fieldDesc
}
bool InstanceKlass::find_local_field_by_name(Symbol* name, fieldDescriptor* fd) const {
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
Symbol* f_name = fs.name();
if (f_name == name) {
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.index());
return true;
}
}
return false;
}
void InstanceKlass::methods_do(void f(Method* method)) {
// Methods aren't stable until they are loaded. This can be read outside
// a lock through the ClassLoaderData for profiling

View File

@@ -566,6 +566,7 @@ public:
bool find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const;
bool find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const;
bool find_local_field_by_name(Symbol* name, fieldDescriptor* fd) const;
private:
inline static int quick_search(const Array<Method*>* methods, const Symbol* name);

View File

@@ -196,13 +196,14 @@ void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word
// The constructor is also used from CppVtableCloner,
// which doesn't zero out the memory before calling the constructor.
Klass::Klass(KlassKind kind) : _kind(kind),
_old_version(NULL),
_new_version(NULL),
_old_version(nullptr),
_new_version(nullptr),
_redefinition_flags(Klass::NoRedefinition),
_is_redefining(false),
_update_information(NULL),
_update_information(nullptr),
_is_copying_backwards(false),
_is_rolled_back(false),
_compat_check_field_offsets(nullptr),
_shared_class_path_index(-1) {
CDS_ONLY(_shared_class_flags = 0;)
CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)

View File

@@ -167,18 +167,18 @@ class Klass : public Metadata {
JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
// Advanced class redefinition
// Enhanced class redefinition
// Old version (used in advanced class redefinition)
Klass* _old_version;
// New version (used in advanced class redefinition)
Klass* _new_version;
int _redefinition_flags; // Level of class redefinition
bool _is_redefining;
int* _update_information;
bool _is_copying_backwards; // Does the class need to copy fields backwards? => possibly overwrite itself?
bool _is_rolled_back; // true if class was rolled back in redefinition
// offsets of fields used in compatibility check
GrowableArray<int>* volatile _compat_check_field_offsets;
private:
// This is an index into FileMapHeader::_shared_path_table[], to
@@ -419,6 +419,13 @@ protected:
void set_copying_backwards(bool b) { _is_copying_backwards = b; }
bool is_rolled_back() { return _is_rolled_back; }
void set_rolled_back(bool b) { _is_rolled_back = b;}
GrowableArray<int>* compat_check_field_offsets() const { return Atomic::load_acquire(&_compat_check_field_offsets); }
GrowableArray<int>* set_compat_check_field_offsets(GrowableArray<int>* offsets) {
return Atomic::cmpxchg(&_compat_check_field_offsets, (GrowableArray<int>*) nullptr, offsets);
}
void clear_compat_check_field_offsets() {
_compat_check_field_offsets = nullptr;
}
protected: // internal accessors
void set_subklass(Klass* s);
@@ -431,7 +438,8 @@ protected:
ModifyClassSize = ModifyClass << 1, // The size of the class meta data changes.
ModifyInstances = ModifyClassSize << 1, // There are change to the instance format.
ModifyInstanceSize = ModifyInstances << 1, // The size of instances changes.
RemoveSuperType = ModifyInstanceSize << 1, // A super type of this class is removed.
RemoveInterface = ModifyInstanceSize << 1, // A super type of this class is removed.
RemoveSuperType = RemoveInterface << 1, // A super type of this class is removed.
MarkedAsAffected = RemoveSuperType << 1, // This class has been marked as an affected class.
PrimaryRedefine = MarkedAsAffected << 1 // This class is from primary redefinition set
};

View File

@@ -108,6 +108,7 @@ VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const j
VM_GC_Operation(Universe::heap()->total_collections(), GCCause::_heap_inspection, Universe::heap()->total_full_collections(), true) {
_new_classes = nullptr;
_affected_klasses = nullptr;
_removed_interfaces = nullptr;
_class_count = class_count;
_class_defs = class_defs;
_class_load_kind = class_load_kind;
@@ -128,7 +129,6 @@ static inline InstanceKlass* get_ik(jclass def) {
// - Start mark&sweep GC.
// - true if success, otherwise all chnages are rollbacked.
bool VM_EnhancedRedefineClasses::doit_prologue() {
if (_class_count == 0) {
_res = JVMTI_ERROR_NONE;
return false;
@@ -202,7 +202,36 @@ bool VM_EnhancedRedefineClasses::doit_prologue() {
// Closer for static fields - copy value from old class to the new class.
class FieldCopier : public FieldClosure {
public:
private:
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
public:
FieldCopier(VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: _removed_interfaces(removed_interfaces) {}
bool is_compatible(oop fld_holder, int fld_offset, Symbol* sig_new) {
oop oop = fld_holder->obj_field(fld_offset);
if (oop != nullptr) {
Klass* k = oop->klass();
if (k->is_instance_klass() && k->is_redefining()) {
InstanceKlass *scan = InstanceKlass::cast(k);
while (scan != nullptr) {
if (sig_new->equals(scan->signature_name())) {
return true;
}
Array<InstanceKlass*>* local_interfaces = scan->local_interfaces();
for (int j = 0; j < local_interfaces->length(); j++) {
Klass* iface = local_interfaces->at(j);
if (sig_new->equals(iface->signature_name())) {
return true;
}
}
scan = (scan->super() != nullptr) ? InstanceKlass::cast(scan->super()) : nullptr;
}
}
}
return false;
}
void do_field(fieldDescriptor* fd) {
InstanceKlass* cur = InstanceKlass::cast(fd->field_holder());
oop cur_oop = cur->java_mirror();
@@ -211,19 +240,36 @@ class FieldCopier : public FieldClosure {
oop old_oop = old->java_mirror();
fieldDescriptor result;
bool found = old->find_local_field(fd->name(), fd->signature(), &result);
Symbol* sig_new = fd->signature();
bool found = old->find_local_field_by_name(fd->name(), &result);
if (found && result.is_static()) {
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
fd->name()->as_C_string(), result.offset(), fd->offset());
memcpy(cur_oop->field_addr<HeapWord>(fd->offset()),
old_oop->field_addr<HeapWord>(result.offset()),
type2aelembytes(fd->field_type()));
Symbol* sig_old = result.signature();
bool compatible = false;
// Static fields may have references to java.lang.Class
if (fd->field_type() == T_OBJECT) {
oop oop = cur_oop->obj_field(fd->offset());
if (oop != nullptr && oop->is_instance() && InstanceKlass::cast(oop->klass())->is_mirror_instance_klass()) {
Klass* klass = java_lang_Class::as_Klass(oop);
if (sig_new == sig_old) {
if (_removed_interfaces != nullptr && _removed_interfaces->contains(sig_old) && fd->field_type() == T_OBJECT && result.field_type() == T_OBJECT) {
compatible = is_compatible(old_oop, result.offset(), sig_new);
} else {
compatible = true;
}
} else {
if (fd->field_type() == T_OBJECT && result.field_type() == T_OBJECT) {
compatible = is_compatible(old_oop, result.offset(), sig_new);
}
}
if (compatible) {
log_trace(redefine, class, obsolete, metadata)("Copying static field value for field %s old_offset=%d new_offset=%d",
fd->name()->as_C_string(), result.offset(), fd->offset());
memcpy(cur_oop->field_addr<HeapWord>(fd->offset()),
old_oop->field_addr<HeapWord>(result.offset()),
type2aelembytes(fd->field_type()));
// Static fields may have references to java.lang.Class
if (fd->field_type() == T_OBJECT) {
oop oop = cur_oop->obj_field(fd->offset());
if (oop != nullptr && oop->is_instance() && InstanceKlass::cast(oop->klass())->is_mirror_instance_klass()) {
Klass *klass = java_lang_Class::as_Klass(oop);
if (klass != nullptr && klass->is_instance_klass()) {
assert(oop == InstanceKlass::cast(klass)->java_mirror(), "just checking");
if (klass->new_version() != nullptr) {
@@ -233,10 +279,36 @@ class FieldCopier : public FieldClosure {
}
}
}
} else {
log_trace(redefine,class, obsolete, metadata)("Skipping incompatible static field %s, old_signature=%s, new_signature=%s",
fd->name()->as_C_string(), sig_old->as_C_string(), sig_new->as_C_string());
}
}
}
};
class FieldCompatibilityChecker : public FieldClosure {
private:
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
GrowableArray<int>* _compat_check_field_offsets;
public:
FieldCompatibilityChecker(VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: _removed_interfaces(removed_interfaces), _compat_check_field_offsets(nullptr) {}
void do_field(fieldDescriptor* fd) {
Symbol *sig_new = fd->signature();
if (_removed_interfaces->contains(fd->signature())) {
if (_compat_check_field_offsets == nullptr) {
_compat_check_field_offsets = new (mtInternal) GrowableArray<int>(2, mtInternal);
}
_compat_check_field_offsets->append(fd->offset());
}
}
GrowableArray<int>* compat_check_field_offsets() {
return _compat_check_field_offsets;
}
};
// TODO: review...
void VM_EnhancedRedefineClasses::mark_as_scavengable(nmethod* nm) {
@@ -442,15 +514,23 @@ class ChangePointersOopClosure : public BasicOopIterateClosure {
// - otherwise set the _needs_instance_update flag, we need to do full GC
// and reshuffle object positions durring mark&sweep
class ChangePointersObjectClosure : public ObjectClosure {
private:
private:
OopIterateClosure *_closure;
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
DcevmSharedGC* _dcevm_shared_gc;
bool _needs_instance_update;
oop _tmp_obj;
size_t _tmp_obj_size;
public:
ChangePointersObjectClosure(OopIterateClosure *closure) : _closure(closure), _needs_instance_update(false), _tmp_obj(nullptr), _tmp_obj_size(0) {}
ChangePointersObjectClosure(OopIterateClosure *closure, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: _closure(closure), _removed_interfaces(removed_interfaces), _needs_instance_update(false), _tmp_obj(nullptr), _tmp_obj_size(0) {
_dcevm_shared_gc = new DcevmSharedGC();
}
~ChangePointersObjectClosure() {
delete _dcevm_shared_gc;
}
bool needs_instance_update() {
return _needs_instance_update;
@@ -465,6 +545,36 @@ public:
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(o), cast_from_oop<HeapWord*>(_tmp_obj), size);
}
void do_compat_check_field_offsets(oop obj) {
// Non-redefined class may store fields of redefined types
// check field-level compatibility to avoid invalid accesses.
GrowableArray<int>* fields = obj->klass()->compat_check_field_offsets();
if (fields == nullptr) {
FieldCompatibilityChecker fld_compat_check(_removed_interfaces);
InstanceKlass::cast(obj->klass())->do_nonstatic_fields(&fld_compat_check);
fields = fld_compat_check.compat_check_field_offsets();
if (fields != nullptr) {
GrowableArray<int>* old = obj->klass()->set_compat_check_field_offsets(fields);
if (old != nullptr) {
delete fields;
fields = old;
}
} else {
fields = reinterpret_cast<GrowableArray<int>*>(-1);
obj->klass()->set_compat_check_field_offsets(fields);
}
}
if (reinterpret_cast<intptr_t>(fields) != -1) {
for (int i = 0; i < fields->length(); i++) {
int fld_offset = fields->at(i);
oop fld_val = obj->obj_field(fld_offset);
if (fld_val != nullptr && !_dcevm_shared_gc->is_compatible(obj, fld_offset, fld_val)) {
obj->obj_field_put(fld_offset, nullptr);
}
}
}
}
virtual void do_object(oop obj) {
if (obj->is_instance() && InstanceKlass::cast(obj->klass())->is_mirror_instance_klass()) {
// static fields may have references to old java.lang.Class instances, update them
@@ -473,6 +583,9 @@ public:
//instanceMirrorKlass::oop_fields_iterate(obj, _closure);
} else {
obj->oop_iterate(_closure);
if (_removed_interfaces != nullptr && obj->klass()->is_instance_klass() && obj->klass()->new_version() == nullptr) {
do_compat_check_field_offsets(obj);
}
}
if (obj->klass()->new_version() != nullptr) {
@@ -482,6 +595,7 @@ public:
if (obj->size() - obj->size_given_klass(new_klass) != 0) {
// We need an instance update => set back to old klass
_needs_instance_update = true;
_dcevm_shared_gc->update_fields_in_old(obj, new_klass->update_information());
} else {
// Either new size is bigger or gap is too small to be filled
oop src = obj;
@@ -492,7 +606,7 @@ public:
src->set_klass(obj->klass()->new_version());
// FIXME: instance updates...
//guarantee(false, "instance updates!");
DcevmSharedGC::update_fields(obj, src, new_klass->update_information());
_dcevm_shared_gc->update_fields(obj, src, new_klass->update_information(), true);
}
} else {
obj->set_klass(obj->klass()->new_version());
@@ -505,14 +619,15 @@ class ChangePointersObjectTask : public WorkerTask {
private:
ChangePointersOopClosure<StoreBarrier>* _cl;
ParallelObjectIterator* _poi;
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
bool _needs_instance_update;
public:
ChangePointersObjectTask(ChangePointersOopClosure<StoreBarrier>* cl, ParallelObjectIterator* poi) : WorkerTask("IterateObject Closure"),
_cl(cl), _poi(poi), _needs_instance_update(false) { }
ChangePointersObjectTask(ChangePointersOopClosure<StoreBarrier>* cl, ParallelObjectIterator* poi, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces)
: WorkerTask("IterateObject Closure"), _cl(cl), _poi(poi), _removed_interfaces(removed_interfaces), _needs_instance_update(false) { }
virtual void work(uint worker_id) {
HandleMark hm(Thread::current()); // make sure any handles created are deleted
ChangePointersObjectClosure objectClosure(_cl);
ChangePointersObjectClosure objectClosure(_cl, _removed_interfaces);
_poi->object_iterate(&objectClosure, worker_id);
_needs_instance_update = _needs_instance_update || objectClosure.needs_instance_update();
}
@@ -521,6 +636,19 @@ public:
}
};
class ClearCompatCheckFields : public KlassClosure {
public:
ClearCompatCheckFields() {}
void do_klass(Klass* k) {
if (k->compat_check_field_offsets() != nullptr) {
if (reinterpret_cast<intptr_t>(k->compat_check_field_offsets()) != -1) {
delete k->compat_check_field_offsets();
}
k->clear_compat_check_field_offsets();
}
}
};
// Main transformation method - runs in VM thread.
// - for each scratch class call redefine_single_class
// - clear code cache (flush_dependent_code)
@@ -552,6 +680,7 @@ void VM_EnhancedRedefineClasses::doit() {
}
Universe::set_inside_redefinition(true);
DcevmSharedGC::create_static_instance();
// Mark methods seen on stack and everywhere else so old methods are not
// cleaned up if they're on the stack.
@@ -639,11 +768,11 @@ void VM_EnhancedRedefineClasses::doit() {
WorkerThreads* workers = Universe::heap()->safepoint_workers();
if (workers != nullptr && workers->active_workers() > 1) {
ParallelObjectIterator poi(workers->active_workers());
ChangePointersObjectTask objectTask(&oopClosure, &poi);
ChangePointersObjectTask objectTask(&oopClosure, &poi, _removed_interfaces);
workers->run_task(&objectTask);
needs_instance_update = objectTask.needs_instance_update();
} else {
ChangePointersObjectClosure objectClosure(&oopClosure);
ChangePointersObjectClosure objectClosure(&oopClosure, _removed_interfaces);
Universe::heap()->object_iterate(&objectClosure);
needs_instance_update = objectClosure.needs_instance_update();
}
@@ -679,7 +808,7 @@ void VM_EnhancedRedefineClasses::doit() {
// Initialize the new class! Special static initialization that does not execute the
// static constructor but copies static field values from the old class if name
// and signature of a static field match.
FieldCopier copier;
FieldCopier copier(_removed_interfaces);
cur->do_local_static_fields(&copier); // TODO (tw): What about internal static fields??
//java_lang_Class::set_klass(old->java_mirror(), cur); // FIXME-isd (from JDK8): is that correct?
//FIXME-isd (from JDK8): do we need this: ??? old->set_java_mirror(cur->java_mirror());
@@ -753,6 +882,12 @@ void VM_EnhancedRedefineClasses::doit() {
cur->clear_update_information();
}
// delete compat_check_fields
if (_removed_interfaces != nullptr) {
ClearCompatCheckFields compat_check_fields;
ClassLoaderDataGraph::classes_do(&compat_check_fields);
}
// TODO: explain...
LoaderConstraintTable::update_after_redefinition();
@@ -790,6 +925,7 @@ void VM_EnhancedRedefineClasses::doit() {
}
#endif
DcevmSharedGC::destroy_static_instance();
Universe::set_inside_redefinition(false);
_timer_vm_op_doit.stop();
@@ -881,9 +1017,10 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
_max_redefinition_flags = Klass::NoRedefinition;
GrowableArray<Klass*>* prev_affected_klasses = new (mtInternal) GrowableArray<Klass*>(_class_count, mtInternal);
GrowableArray<int> klass_redefinition_flags(_class_count, mtInternal);
do {
err = load_new_class_versions_single_step(&old_2_new_klass_map, THREAD);
err = load_new_class_versions_single_step(&old_2_new_klass_map, &klass_redefinition_flags, THREAD);
if (err != JVMTI_ERROR_NONE) {
delete prev_affected_klasses;
return err;
@@ -906,6 +1043,15 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
delete _affected_klasses;
_affected_klasses = prev_affected_klasses;
// Calculate instance update information after all new classes are resolved
for (int i = 0; i < _new_classes->length(); i++) {
int redefinition_flags = klass_redefinition_flags.at(i);
if ((redefinition_flags & Klass::ModifyInstances) != 0) {
Klass *new_class = _new_classes->at(i);
calculate_instance_update_information(new_class);
}
}
// Link and verify new classes _after_ all classes have been updated in the system dictionary!
for (int i = 0; i < _affected_klasses->length(); i++) {
Klass* the_class = _affected_klasses->at(i);
@@ -928,7 +1074,8 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions(TRAPS) {
return JVMTI_ERROR_NONE;
}
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, TRAPS) {
jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map,
GrowableArray<int>* klass_redefinition_flags, TRAPS) {
// thread local state - used to transfer class_being_redefined object to SystemDictonery::resolve_from_stream
JvmtiThreadState *state = JvmtiThreadState::state_for(JavaThread::current());
@@ -1146,9 +1293,7 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(Old2N
_max_redefinition_flags = _max_redefinition_flags | redefinition_flags;
if ((redefinition_flags & Klass::ModifyInstances) != 0) {
calculate_instance_update_information(new_class);
}
klass_redefinition_flags->append(redefinition_flags);
if (the_class == vmClasses::Object_klass()) {
_object_klass_redefined = true;
@@ -1195,7 +1340,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
cur_klass = new_class->super();
while (cur_klass != nullptr) {
if (!the_class->is_subclass_of(cur_klass->is_redefining() ? cur_klass->old_version() : cur_klass)) {
log_info(redefine, class, load)("added super class %s", cur_klass->name()->as_C_string());
log_debug(redefine, class, load)("added super class %s", cur_klass->name()->as_C_string());
result = result | Klass::ModifyClass | Klass::ModifyInstances;
}
cur_klass = cur_klass->super();
@@ -1209,8 +1354,13 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
for (i = 0; i < old_interfaces->length(); i++) {
InstanceKlass* old_interface = InstanceKlass::cast(old_interfaces->at(i));
if (!new_class->implements_interface_dcevm(old_interface, old_2_new_klass_map)) {
result = result | Klass::RemoveSuperType | Klass::ModifyClass;
log_info(redefine, class, load)("removed interface %s", old_interface->name()->as_C_string());
result = result | Klass::RemoveInterface | Klass::ModifyClass;
log_debug(redefine, class, load)("removed interface %s", old_interface->name()->as_C_string());
if (_removed_interfaces == nullptr) {
_removed_interfaces = new (mtInternal) SymbolSet();
}
Symbol* interf_sign_sym = SymbolTable::new_symbol(old_interface->signature_name());
_removed_interfaces->put(interf_sign_sym, true);
}
}
@@ -1219,7 +1369,7 @@ int VM_EnhancedRedefineClasses::calculate_redefinition_flags(InstanceKlass* new_
for (i = 0; i<new_interfaces->length(); i++) {
if (!the_class->implements_interface_dcevm(new_interfaces->at(i), old_2_new_klass_map)) {
result = result | Klass::ModifyClass;
log_info(redefine, class, load)("added interface %s", new_interfaces->at(i)->name()->as_C_string());
log_debug(redefine, class, load)("added interface %s", new_interfaces->at(i)->name()->as_C_string());
}
}
@@ -1495,7 +1645,7 @@ jvmtiError VM_EnhancedRedefineClasses::find_class_bytes(InstanceKlass* the_class
// Calculate difference between non static fields of old and new class and store the info into new class:
// instanceKlass->store_update_information
// instanceKlass->copy_backwards
// instanceKlass->copying_backwards
void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* new_version) {
class CalculateFieldUpdates : public FieldClosure {
@@ -1503,17 +1653,24 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
private:
InstanceKlass* _old_ik;
GrowableArray<int> _update_info;
VM_EnhancedRedefineClasses::SymbolSet* _removed_interfaces;
int _position;
bool _copy_backwards;
bool _copying_backwards;
bool _compat_check;
public:
bool does_copy_backwards() {
return _copy_backwards;
bool is_copying_backwards() {
return _copying_backwards;
}
CalculateFieldUpdates(InstanceKlass* old_ik) :
_old_ik(old_ik), _position(instanceOopDesc::base_offset_in_bytes()), _copy_backwards(false) {
bool is_compat_check() {
return _compat_check;
}
CalculateFieldUpdates(InstanceKlass* old_ik, VM_EnhancedRedefineClasses::SymbolSet* removed_interfaces) :
_old_ik(old_ik), _removed_interfaces(removed_interfaces), _position(instanceOopDesc::base_offset_in_bytes()),
_copying_backwards(false), _compat_check() {
_update_info.append(_position);
_update_info.append(0);
}
@@ -1535,43 +1692,78 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
assert(_position == fd->offset(), "must be correct offset!");
InstanceKlass* holder = fd->field_holder();
InstanceKlass* maybe_old_holder = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
if (fd->index() < holder->java_fields_count()) {
fieldDescriptor old_fd;
if (_old_ik->find_field(fd->name(), fd->signature(), false, &old_fd) != nullptr) {
// Found field in the old class, copy
copy(old_fd.offset(), type2aelembytes(fd->field_type()));
if (old_fd.offset() < fd->offset()) {
_copy_backwards = true;
bool found = false;
if (_old_ik->find_field(fd->name(), fd->signature(), false, &old_fd) != nullptr) {
found = true;
} else {
if (maybe_old_holder->find_local_field_by_name(fd->name(), &old_fd) && !old_fd.is_static()) {
found = true;
}
}
if (found) {
// Found field in the old class, copy
Symbol *sig_new = fd->signature();
Symbol *sig_old = old_fd.signature();
int compat_flag;
if (sig_new == sig_old) {
if (_removed_interfaces != nullptr && _removed_interfaces->contains(sig_old) && fd->field_type() == T_OBJECT && old_fd.field_type() == T_OBJECT) {
compat_flag = 0;
_compat_check = true;
} else {
compat_flag = 1;
}
} else {
if (fd->field_type() == T_OBJECT && old_fd.field_type() == T_OBJECT) {
compat_flag = 0;
_compat_check = true;
} else {
compat_flag = -1;
}
}
// Transfer special flags
fd->set_is_field_modification_watched(old_fd.is_field_modification_watched());
fd->set_is_field_access_watched(old_fd.is_field_access_watched());
if (compat_flag != -1) {
copy(old_fd.offset(), type2aelembytes(fd->field_type()), (compat_flag == 0));
if (old_fd.offset() < fd->offset()) {
_copying_backwards = true;
}
// Transfer special flags
fd->set_is_field_modification_watched(old_fd.is_field_modification_watched());
fd->set_is_field_access_watched(old_fd.is_field_access_watched());
} else {
fill(type2aelembytes(fd->field_type()));
}
} else {
// New field, fill
fill(type2aelembytes(fd->field_type()));
}
} else {
FieldInfo internal_field = holder->field(fd->index());
InstanceKlass* maybe_old_klass = holder->is_redefining() ? InstanceKlass::cast(holder->old_version()) : holder;
int java_fields_count = maybe_old_klass->java_fields_count();
int java_fields_count = maybe_old_holder->java_fields_count();
int num_injected;
const InjectedField* const injected = JavaClasses::get_injected(maybe_old_klass->name(), &num_injected);
for (int i = java_fields_count; i < java_fields_count+num_injected; i++) {
FieldInfo maybe_old_field = maybe_old_klass->field(i);
const InjectedField *const injected = JavaClasses::get_injected(maybe_old_holder->name(), &num_injected);
for (int i = java_fields_count; i < java_fields_count + num_injected; i++) {
FieldInfo maybe_old_field = maybe_old_holder->field(i);
if (maybe_old_field.field_flags().is_injected() &&
internal_field.field_flags().is_injected() &&
maybe_old_field.lookup_symbol(maybe_old_field.name_index()) == internal_field.lookup_symbol(internal_field.name_index())) {
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())));
copy(maybe_old_field.offset(), type2aelembytes(Signature::basic_type(internal_field.signature_injected_dcevm())), false);
if (maybe_old_field.offset() < internal_field.offset()) {
_copy_backwards = true;
_copying_backwards = true;
}
break;
}
}
}
}
}
private:
void fill(int size) {
@@ -1583,19 +1775,24 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
_position += size;
}
void copy(int offset, int size) {
int prev_end = -1;
if (_update_info.length() > 0 && _update_info.at(_update_info.length() - 1) > 0) {
prev_end = _update_info.at(_update_info.length() - 2) + _update_info.at(_update_info.length() - 1);
}
void copy(int offset, int size, bool needs_compat_check) {
if (!needs_compat_check && _update_info.length() >= 2) {
int last_size = _update_info.at(_update_info.length() - 2);
int last_offset = _update_info.at(_update_info.length() - 1);
if (prev_end == offset) {
(*_update_info.adr_at(_update_info.length() - 2)) += size;
} else {
_update_info.append(size);
_update_info.append(offset);
if (last_offset > 0 && (last_size & DcevmSharedGC::UpdateInfoCompatFlag) == 0) {
int last_len = last_size & DcevmSharedGC::UpdateInfoLengthMask;
int prev_end = last_offset + last_len;
if (prev_end == offset) {
(*_update_info.adr_at(_update_info.length() - 2)) += size;
_position += size;
return;
}
}
}
int tagged_size = needs_compat_check ? (size | DcevmSharedGC::UpdateInfoCompatFlag) : size;
_update_info.append(tagged_size);
_update_info.append(offset);
_position += size;
}
};
@@ -1604,15 +1801,16 @@ void VM_EnhancedRedefineClasses::calculate_instance_update_information(Klass* ne
InstanceKlass* old_ik = InstanceKlass::cast(new_version->old_version());
//
CalculateFieldUpdates cl(old_ik);
CalculateFieldUpdates cl(old_ik, _removed_interfaces);
ik->do_nonstatic_fields_dcevm(&cl);
GrowableArray<int> result = cl.finish();
ik->store_update_information(result);
ik->set_copying_backwards(cl.does_copy_backwards());
ik->set_copying_backwards(cl.is_copying_backwards());
if (log_is_enabled(Trace, redefine, class, obsolete, metadata)) {
log_trace(redefine, class, obsolete, metadata)("Instance update information for %s:", new_version->name()->as_C_string());
if (cl.does_copy_backwards()) {
if (cl.is_copying_backwards()) {
log_trace(redefine, class, obsolete, metadata)("\tDoes copy backwards!");
}
for (int i=0; i<result.length(); i++) {
@@ -1777,21 +1975,24 @@ u8 VM_EnhancedRedefineClasses::next_id() {
}
// Clean method data for this class
void VM_EnhancedRedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
if (k->is_instance_klass()) {
InstanceKlass *ik = InstanceKlass::cast(k);
// Clean MethodData of this class's methods so they don't refer to
// old methods that are no longer running.
Array<Method*>* methods = ik->methods();
int num_methods = methods->length();
for (int index = 0; index < num_methods; ++index) {
if (methods->at(index)->method_data() != nullptr) {
methods->at(index)->method_data()->clean_weak_method_links();
class MethodDataCleaner : public KlassClosure {
public:
MethodDataCleaner() {}
void do_klass(Klass* k) {
if (k->is_instance_klass()) {
InstanceKlass *ik = InstanceKlass::cast(k);
// Clean MethodData of this class's methods so they don't refer to
// old methods that are no longer running.
Array<Method*>* methods = ik->methods();
int num_methods = methods->length();
for (int index = 0; index < num_methods; ++index) {
if (methods->at(index)->method_data() != nullptr) {
methods->at(index)->method_data()->clean_weak_method_links();
}
}
}
}
}
};
void VM_EnhancedRedefineClasses::update_jmethod_ids(Thread *current) {
for (int j = 0; j < _matching_methods_length; ++j) {

View File

@@ -47,7 +47,11 @@
// - doit() - main redefition, adjust existing objects on the heap, clear caches
// - doit_epilogue() - cleanup
class VM_EnhancedRedefineClasses: public VM_GC_Operation {
private:
public:
static unsigned int sym_hash (Symbol* const& s) { return (int)(uintptr_t)s; }
static bool sym_equals(Symbol* const& a, Symbol* const& b) { return a == b; }
typedef ResourceHashtable<Symbol*, char, 37, AnyObj::C_HEAP, mtInternal, &sym_hash, &sym_equals> SymbolSet;
private:
// These static fields are needed by ClassLoaderDataGraph::classes_do()
// facility and the AdjustCpoolCacheAndVtable helper:
static Array<Method*>* _old_methods;
@@ -71,7 +75,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
// RetransformClasses. Indicate which.
JvmtiClassLoadKind _class_load_kind;
GrowableArray<InstanceKlass*>* _new_classes;
GrowableArray<InstanceKlass*>* _new_classes;
jvmtiError _res;
// Set if any of the InstanceKlasses have entries in the ResolvedMethodTable
@@ -90,6 +94,8 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
int _max_redefinition_flags;
SymbolSet* _removed_interfaces;
// Performance measurement support. These timers do not cover all
// the work done for JVM/TI RedefineClasses() but they do cover
// the heavy lifting.
@@ -113,7 +119,7 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
//
// The result is sotred in _affected_klasses(old definitions) and _new_classes(new definitions) arrays.
jvmtiError load_new_class_versions(TRAPS);
jvmtiError load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, TRAPS);
jvmtiError load_new_class_versions_single_step(Old2NewKlassMap* old_2_new_klass_map, GrowableArray<int>* klass_redefinition_flags, TRAPS);
// Searches for all affected classes and performs a sorting such tha
// a supertype is always before a subtype.
@@ -171,13 +177,6 @@ class VM_EnhancedRedefineClasses: public VM_GC_Operation {
ClearCpoolCacheAndUnpatch(Thread* t) : _thread(t) {}
void do_klass(Klass* k);
};
// Clean MethodData out
class MethodDataCleaner : public KlassClosure {
public:
MethodDataCleaner() {}
void do_klass(Klass* k);
};
public:
VM_EnhancedRedefineClasses(jint class_count,
const jvmtiClassDefinition *class_defs,