mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-19 10:34:38 +02:00
6898462: The escape analysis with G1 cause crash assertion src/share/vm/runtime/vframeArray.cpp:94
OOM during reallocation of scalar replaced objects in deoptimization causes crashes Reviewed-by: kvn, jrose
This commit is contained in:
parent
53f42d325b
commit
15dcd41e87
12 changed files with 602 additions and 64 deletions
|
@ -176,6 +176,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
assert(vf->is_compiled_frame(), "Wrong frame type");
|
||||
chunk->push(compiledVFrame::cast(vf));
|
||||
|
||||
bool realloc_failures = false;
|
||||
|
||||
#ifdef COMPILER2
|
||||
// Reallocate the non-escaping objects and restore their fields. Then
|
||||
// relock objects if synchronization on them was eliminated.
|
||||
|
@ -206,22 +208,19 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, (void *)result, thread);
|
||||
}
|
||||
}
|
||||
bool reallocated = false;
|
||||
if (objects != NULL) {
|
||||
JRT_BLOCK
|
||||
reallocated = realloc_objects(thread, &deoptee, objects, THREAD);
|
||||
realloc_failures = realloc_objects(thread, &deoptee, objects, THREAD);
|
||||
JRT_END
|
||||
reassign_fields(&deoptee, &map, objects, realloc_failures);
|
||||
}
|
||||
if (reallocated) {
|
||||
reassign_fields(&deoptee, &map, objects);
|
||||
#ifndef PRODUCT
|
||||
if (TraceDeoptimization) {
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread);
|
||||
print_objects(objects);
|
||||
}
|
||||
#endif
|
||||
if (TraceDeoptimization) {
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread);
|
||||
print_objects(objects, realloc_failures);
|
||||
}
|
||||
#endif
|
||||
if (save_oop_result) {
|
||||
// Restore result.
|
||||
deoptee.set_saved_oop_result(&map, return_value());
|
||||
|
@ -236,7 +235,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
assert (cvf->scope() != NULL,"expect only compiled java frames");
|
||||
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
|
||||
if (monitors->is_nonempty()) {
|
||||
relock_objects(monitors, thread);
|
||||
relock_objects(monitors, thread, realloc_failures);
|
||||
#ifndef PRODUCT
|
||||
if (TraceDeoptimization) {
|
||||
ttyLocker ttyl;
|
||||
|
@ -247,7 +246,12 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
first = false;
|
||||
tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread);
|
||||
}
|
||||
tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner());
|
||||
if (mi->owner_is_scalar_replaced()) {
|
||||
Klass* k = java_lang_Class::as_Klass(mi->owner_klass());
|
||||
tty->print_cr(" failed reallocation for klass %s", k->external_name());
|
||||
} else {
|
||||
tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,9 +266,14 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
// out the java state residing in the vframeArray will be missed.
|
||||
No_Safepoint_Verifier no_safepoint;
|
||||
|
||||
vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk);
|
||||
vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk, realloc_failures);
|
||||
#ifdef COMPILER2
|
||||
if (realloc_failures) {
|
||||
pop_frames_failed_reallocs(thread, array);
|
||||
}
|
||||
#endif
|
||||
|
||||
assert(thread->vframe_array_head() == NULL, "Pending deopt!");;
|
||||
assert(thread->vframe_array_head() == NULL, "Pending deopt!");
|
||||
thread->set_vframe_array_head(array);
|
||||
|
||||
// Now that the vframeArray has been created if we have any deferred local writes
|
||||
|
@ -718,6 +727,8 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra
|
|||
int exception_line = thread->exception_line();
|
||||
thread->clear_pending_exception();
|
||||
|
||||
bool failures = false;
|
||||
|
||||
for (int i = 0; i < objects->length(); i++) {
|
||||
assert(objects->at(i)->is_object(), "invalid debug information");
|
||||
ObjectValue* sv = (ObjectValue*) objects->at(i);
|
||||
|
@ -727,27 +738,34 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra
|
|||
|
||||
if (k->oop_is_instance()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k());
|
||||
obj = ik->allocate_instance(CHECK_(false));
|
||||
obj = ik->allocate_instance(THREAD);
|
||||
} else if (k->oop_is_typeArray()) {
|
||||
TypeArrayKlass* ak = TypeArrayKlass::cast(k());
|
||||
assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length");
|
||||
int len = sv->field_size() / type2size[ak->element_type()];
|
||||
obj = ak->allocate(len, CHECK_(false));
|
||||
obj = ak->allocate(len, THREAD);
|
||||
} else if (k->oop_is_objArray()) {
|
||||
ObjArrayKlass* ak = ObjArrayKlass::cast(k());
|
||||
obj = ak->allocate(sv->field_size(), CHECK_(false));
|
||||
obj = ak->allocate(sv->field_size(), THREAD);
|
||||
}
|
||||
|
||||
if (obj == NULL) {
|
||||
failures = true;
|
||||
}
|
||||
|
||||
assert(obj != NULL, "allocation failed");
|
||||
assert(sv->value().is_null(), "redundant reallocation");
|
||||
assert(obj != NULL || HAS_PENDING_EXCEPTION, "allocation should succeed or we should get an exception");
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
sv->set_value(obj);
|
||||
}
|
||||
|
||||
if (pending_exception.not_null()) {
|
||||
if (failures) {
|
||||
THROW_OOP_(Universe::out_of_memory_error_realloc_objects(), failures);
|
||||
} else if (pending_exception.not_null()) {
|
||||
thread->set_pending_exception(pending_exception(), exception_file, exception_line);
|
||||
}
|
||||
|
||||
return true;
|
||||
return failures;
|
||||
}
|
||||
|
||||
// This assumes that the fields are stored in ObjectValue in the same order
|
||||
|
@ -885,12 +903,15 @@ void Deoptimization::reassign_object_array_elements(frame* fr, RegisterMap* reg_
|
|||
|
||||
|
||||
// restore fields of all eliminated objects and arrays
|
||||
void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects) {
|
||||
void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures) {
|
||||
for (int i = 0; i < objects->length(); i++) {
|
||||
ObjectValue* sv = (ObjectValue*) objects->at(i);
|
||||
KlassHandle k(java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()()));
|
||||
Handle obj = sv->value();
|
||||
assert(obj.not_null(), "reallocation was missed");
|
||||
assert(obj.not_null() || realloc_failures, "reallocation was missed");
|
||||
if (obj.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k->oop_is_instance()) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(k());
|
||||
|
@ -907,34 +928,36 @@ void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableAr
|
|||
|
||||
|
||||
// relock objects for which synchronization was eliminated
|
||||
void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread) {
|
||||
void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread, bool realloc_failures) {
|
||||
for (int i = 0; i < monitors->length(); i++) {
|
||||
MonitorInfo* mon_info = monitors->at(i);
|
||||
if (mon_info->eliminated()) {
|
||||
assert(mon_info->owner() != NULL, "reallocation was missed");
|
||||
Handle obj = Handle(mon_info->owner());
|
||||
markOop mark = obj->mark();
|
||||
if (UseBiasedLocking && mark->has_bias_pattern()) {
|
||||
// New allocated objects may have the mark set to anonymously biased.
|
||||
// Also the deoptimized method may called methods with synchronization
|
||||
// where the thread-local object is bias locked to the current thread.
|
||||
assert(mark->is_biased_anonymously() ||
|
||||
mark->biased_locker() == thread, "should be locked to current thread");
|
||||
// Reset mark word to unbiased prototype.
|
||||
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
|
||||
obj->set_mark(unbiased_prototype);
|
||||
assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed");
|
||||
if (!mon_info->owner_is_scalar_replaced()) {
|
||||
Handle obj = Handle(mon_info->owner());
|
||||
markOop mark = obj->mark();
|
||||
if (UseBiasedLocking && mark->has_bias_pattern()) {
|
||||
// New allocated objects may have the mark set to anonymously biased.
|
||||
// Also the deoptimized method may called methods with synchronization
|
||||
// where the thread-local object is bias locked to the current thread.
|
||||
assert(mark->is_biased_anonymously() ||
|
||||
mark->biased_locker() == thread, "should be locked to current thread");
|
||||
// Reset mark word to unbiased prototype.
|
||||
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
|
||||
obj->set_mark(unbiased_prototype);
|
||||
}
|
||||
BasicLock* lock = mon_info->lock();
|
||||
ObjectSynchronizer::slow_enter(obj, lock, thread);
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
}
|
||||
BasicLock* lock = mon_info->lock();
|
||||
ObjectSynchronizer::slow_enter(obj, lock, thread);
|
||||
}
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef PRODUCT
|
||||
// print information about reallocated objects
|
||||
void Deoptimization::print_objects(GrowableArray<ScopeValue*>* objects) {
|
||||
void Deoptimization::print_objects(GrowableArray<ScopeValue*>* objects, bool realloc_failures) {
|
||||
fieldDescriptor fd;
|
||||
|
||||
for (int i = 0; i < objects->length(); i++) {
|
||||
|
@ -944,10 +967,15 @@ void Deoptimization::print_objects(GrowableArray<ScopeValue*>* objects) {
|
|||
|
||||
tty->print(" object <" INTPTR_FORMAT "> of type ", (void *)sv->value()());
|
||||
k->print_value();
|
||||
tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize);
|
||||
assert(obj.not_null() || realloc_failures, "reallocation was missed");
|
||||
if (obj.is_null()) {
|
||||
tty->print(" allocation failed");
|
||||
} else {
|
||||
tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize);
|
||||
}
|
||||
tty->cr();
|
||||
|
||||
if (Verbose) {
|
||||
if (Verbose && !obj.is_null()) {
|
||||
k->oop_print_on(obj(), tty);
|
||||
}
|
||||
}
|
||||
|
@ -955,7 +983,7 @@ void Deoptimization::print_objects(GrowableArray<ScopeValue*>* objects) {
|
|||
#endif
|
||||
#endif // COMPILER2
|
||||
|
||||
vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray<compiledVFrame*>* chunk) {
|
||||
vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures) {
|
||||
Events::log(thread, "DEOPT PACKING pc=" INTPTR_FORMAT " sp=" INTPTR_FORMAT, fr.pc(), fr.sp());
|
||||
|
||||
#ifndef PRODUCT
|
||||
|
@ -998,7 +1026,7 @@ vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, Re
|
|||
// Since the Java thread being deoptimized will eventually adjust it's own stack,
|
||||
// the vframeArray containing the unpacking information is allocated in the C heap.
|
||||
// For Compiler1, the caller of the deoptimized frame is saved for use by unpack_frames().
|
||||
vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr);
|
||||
vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr, realloc_failures);
|
||||
|
||||
// Compare the vframeArray to the collected vframes
|
||||
assert(array->structural_compare(thread, chunk), "just checking");
|
||||
|
@ -1013,6 +1041,33 @@ vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, Re
|
|||
return array;
|
||||
}
|
||||
|
||||
#ifdef COMPILER2
|
||||
void Deoptimization::pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array) {
|
||||
// Reallocation of some scalar replaced objects failed. Record
|
||||
// that we need to pop all the interpreter frames for the
|
||||
// deoptimized compiled frame.
|
||||
assert(thread->frames_to_pop_failed_realloc() == 0, "missed frames to pop?");
|
||||
thread->set_frames_to_pop_failed_realloc(array->frames());
|
||||
// Unlock all monitors here otherwise the interpreter will see a
|
||||
// mix of locked and unlocked monitors (because of failed
|
||||
// reallocations of synchronized objects) and be confused.
|
||||
for (int i = 0; i < array->frames(); i++) {
|
||||
MonitorChunk* monitors = array->element(i)->monitors();
|
||||
if (monitors != NULL) {
|
||||
for (int j = 0; j < monitors->number_of_monitors(); j++) {
|
||||
BasicObjectLock* src = monitors->at(j);
|
||||
if (src->obj() != NULL) {
|
||||
ObjectSynchronizer::fast_exit(src->obj(), src->lock(), thread);
|
||||
}
|
||||
}
|
||||
array->element(i)->free_monitors(thread);
|
||||
#ifdef ASSERT
|
||||
array->element(i)->set_removed_monitors();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects_to_revoke) {
|
||||
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue