Tue, 25 Nov 2014 17:33:59 +0100
6898462: The escape analysis with G1 cause crash assertion src/share/vm/runtime/vframeArray.cpp:94
Summary: OOM during reallocation of scalar replaced objects in deoptimization causes crashes
Reviewed-by: kvn, jrose
1.1 --- a/src/share/vm/interpreter/interpreterRuntime.cpp Thu Dec 11 10:38:17 2014 +0000 1.2 +++ b/src/share/vm/interpreter/interpreterRuntime.cpp Tue Nov 25 17:33:59 2014 +0100 1.3 @@ -398,6 +398,18 @@ 1.4 int handler_bci; 1.5 int current_bci = bci(thread); 1.6 1.7 + if (thread->frames_to_pop_failed_realloc() > 0) { 1.8 + // Allocation of scalar replaced object used in this frame 1.9 + // failed. Unconditionally pop the frame. 1.10 + thread->dec_frames_to_pop_failed_realloc(); 1.11 + thread->set_vm_result(h_exception()); 1.12 + // If the method is synchronized we already unlocked the monitor 1.13 + // during deoptimization so the interpreter needs to skip it when 1.14 + // the frame is popped. 1.15 + thread->set_do_not_unlock_if_synchronized(true); 1.16 + return Interpreter::remove_activation_entry(); 1.17 + } 1.18 + 1.19 // Need to do this check first since when _do_not_unlock_if_synchronized 1.20 // is set, we don't want to trigger any classloading which may make calls 1.21 // into java, or surprisingly find a matching exception handler for bci 0
2.1 --- a/src/share/vm/memory/universe.cpp Thu Dec 11 10:38:17 2014 +0000 2.2 +++ b/src/share/vm/memory/universe.cpp Tue Nov 25 17:33:59 2014 +0100 2.3 @@ -119,6 +119,7 @@ 2.4 oop Universe::_out_of_memory_error_class_metaspace = NULL; 2.5 oop Universe::_out_of_memory_error_array_size = NULL; 2.6 oop Universe::_out_of_memory_error_gc_overhead_limit = NULL; 2.7 +oop Universe::_out_of_memory_error_realloc_objects = NULL; 2.8 objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL; 2.9 volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0; 2.10 bool Universe::_verify_in_progress = false; 2.11 @@ -190,6 +191,7 @@ 2.12 f->do_oop((oop*)&_out_of_memory_error_class_metaspace); 2.13 f->do_oop((oop*)&_out_of_memory_error_array_size); 2.14 f->do_oop((oop*)&_out_of_memory_error_gc_overhead_limit); 2.15 + f->do_oop((oop*)&_out_of_memory_error_realloc_objects); 2.16 f->do_oop((oop*)&_preallocated_out_of_memory_error_array); 2.17 f->do_oop((oop*)&_null_ptr_exception_instance); 2.18 f->do_oop((oop*)&_arithmetic_exception_instance); 2.19 @@ -574,7 +576,8 @@ 2.20 (throwable() != Universe::_out_of_memory_error_metaspace) && 2.21 (throwable() != Universe::_out_of_memory_error_class_metaspace) && 2.22 (throwable() != Universe::_out_of_memory_error_array_size) && 2.23 - (throwable() != Universe::_out_of_memory_error_gc_overhead_limit)); 2.24 + (throwable() != Universe::_out_of_memory_error_gc_overhead_limit) && 2.25 + (throwable() != Universe::_out_of_memory_error_realloc_objects)); 2.26 } 2.27 2.28 2.29 @@ -1044,6 +1047,7 @@ 2.30 Universe::_out_of_memory_error_array_size = k_h->allocate_instance(CHECK_false); 2.31 Universe::_out_of_memory_error_gc_overhead_limit = 2.32 k_h->allocate_instance(CHECK_false); 2.33 + Universe::_out_of_memory_error_realloc_objects = k_h->allocate_instance(CHECK_false); 2.34 2.35 // Setup preallocated NullPointerException 2.36 // (this is currently used for a cheap & dirty solution in compiler exception handling) 2.37 @@ -1083,6 +1087,9 @@ 2.38 msg = java_lang_String::create_from_str("GC overhead limit exceeded", CHECK_false); 2.39 java_lang_Throwable::set_message(Universe::_out_of_memory_error_gc_overhead_limit, msg()); 2.40 2.41 + msg = java_lang_String::create_from_str("Java heap space: failed reallocation of scalar replaced objects", CHECK_false); 2.42 + java_lang_Throwable::set_message(Universe::_out_of_memory_error_realloc_objects, msg()); 2.43 + 2.44 msg = java_lang_String::create_from_str("/ by zero", CHECK_false); 2.45 java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg()); 2.46
3.1 --- a/src/share/vm/memory/universe.hpp Thu Dec 11 10:38:17 2014 +0000 3.2 +++ b/src/share/vm/memory/universe.hpp Tue Nov 25 17:33:59 2014 +0100 3.3 @@ -157,6 +157,7 @@ 3.4 static oop _out_of_memory_error_class_metaspace; 3.5 static oop _out_of_memory_error_array_size; 3.6 static oop _out_of_memory_error_gc_overhead_limit; 3.7 + static oop _out_of_memory_error_realloc_objects; 3.8 3.9 static Array<int>* _the_empty_int_array; // Canonicalized int array 3.10 static Array<u2>* _the_empty_short_array; // Canonicalized short array 3.11 @@ -328,6 +329,7 @@ 3.12 static oop out_of_memory_error_class_metaspace() { return gen_out_of_memory_error(_out_of_memory_error_class_metaspace); } 3.13 static oop out_of_memory_error_array_size() { return gen_out_of_memory_error(_out_of_memory_error_array_size); } 3.14 static oop out_of_memory_error_gc_overhead_limit() { return gen_out_of_memory_error(_out_of_memory_error_gc_overhead_limit); } 3.15 + static oop out_of_memory_error_realloc_objects() { return gen_out_of_memory_error(_out_of_memory_error_realloc_objects); } 3.16 3.17 // Accessors needed for fast allocation 3.18 static Klass** boolArrayKlassObj_addr() { return &_boolArrayKlassObj; }
4.1 --- a/src/share/vm/opto/macro.cpp Thu Dec 11 10:38:17 2014 +0000 4.2 +++ b/src/share/vm/opto/macro.cpp Tue Nov 25 17:33:59 2014 +0100 4.3 @@ -964,7 +964,11 @@ 4.4 } 4.5 4.6 bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { 4.7 - if (!EliminateAllocations || !alloc->_is_non_escaping) { 4.8 + // Don't do scalar replacement if the frame can be popped by JVMTI: 4.9 + // if reallocation fails during deoptimization we'll pop all 4.10 + // interpreter frames for this compiled frame and that won't play 4.11 + // nice with JVMTI popframe. 4.12 + if (!EliminateAllocations || JvmtiExport::can_pop_frame() || !alloc->_is_non_escaping) { 4.13 return false; 4.14 } 4.15 Node* klass = alloc->in(AllocateNode::KlassNode);
5.1 --- a/src/share/vm/runtime/deoptimization.cpp Thu Dec 11 10:38:17 2014 +0000 5.2 +++ b/src/share/vm/runtime/deoptimization.cpp Tue Nov 25 17:33:59 2014 +0100 5.3 @@ -213,6 +213,8 @@ 5.4 assert(vf->is_compiled_frame(), "Wrong frame type"); 5.5 chunk->push(compiledVFrame::cast(vf)); 5.6 5.7 + bool realloc_failures = false; 5.8 + 5.9 #ifdef COMPILER2 5.10 // Reallocate the non-escaping objects and restore their fields. Then 5.11 // relock objects if synchronization on them was eliminated. 5.12 @@ -243,22 +245,19 @@ 5.13 tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, (void *)result, thread); 5.14 } 5.15 } 5.16 - bool reallocated = false; 5.17 if (objects != NULL) { 5.18 JRT_BLOCK 5.19 - reallocated = realloc_objects(thread, &deoptee, objects, THREAD); 5.20 + realloc_failures = realloc_objects(thread, &deoptee, objects, THREAD); 5.21 JRT_END 5.22 + reassign_fields(&deoptee, &map, objects, realloc_failures); 5.23 } 5.24 - if (reallocated) { 5.25 - reassign_fields(&deoptee, &map, objects); 5.26 #ifndef PRODUCT 5.27 - if (TraceDeoptimization) { 5.28 - ttyLocker ttyl; 5.29 - tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); 5.30 - print_objects(objects); 5.31 - } 5.32 + if (TraceDeoptimization) { 5.33 + ttyLocker ttyl; 5.34 + tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); 5.35 + print_objects(objects, realloc_failures); 5.36 + } 5.37 #endif 5.38 - } 5.39 if (save_oop_result) { 5.40 // Restore result. 5.41 deoptee.set_saved_oop_result(&map, return_value()); 5.42 @@ -273,7 +272,7 @@ 5.43 assert (cvf->scope() != NULL,"expect only compiled java frames"); 5.44 GrowableArray<MonitorInfo*>* monitors = cvf->monitors(); 5.45 if (monitors->is_nonempty()) { 5.46 - relock_objects(monitors, thread); 5.47 + relock_objects(monitors, thread, realloc_failures); 5.48 #ifndef PRODUCT 5.49 if (TraceDeoptimization) { 5.50 ttyLocker ttyl; 5.51 @@ -284,7 +283,12 @@ 5.52 first = false; 5.53 tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread); 5.54 } 5.55 - tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner()); 5.56 + if (mi->owner_is_scalar_replaced()) { 5.57 + Klass* k = java_lang_Class::as_Klass(mi->owner_klass()); 5.58 + tty->print_cr(" failed reallocation for klass %s", k->external_name()); 5.59 + } else { 5.60 + tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner()); 5.61 + } 5.62 } 5.63 } 5.64 } 5.65 @@ -299,9 +303,14 @@ 5.66 // out the java state residing in the vframeArray will be missed. 5.67 No_Safepoint_Verifier no_safepoint; 5.68 5.69 - vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk); 5.70 + vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk, realloc_failures); 5.71 +#ifdef COMPILER2 5.72 + if (realloc_failures) { 5.73 + pop_frames_failed_reallocs(thread, array); 5.74 + } 5.75 +#endif 5.76 5.77 - assert(thread->vframe_array_head() == NULL, "Pending deopt!");; 5.78 + assert(thread->vframe_array_head() == NULL, "Pending deopt!"); 5.79 thread->set_vframe_array_head(array); 5.80 5.81 // Now that the vframeArray has been created if we have any deferred local writes 5.82 @@ -753,6 +762,8 @@ 5.83 int exception_line = thread->exception_line(); 5.84 thread->clear_pending_exception(); 5.85 5.86 + bool failures = false; 5.87 + 5.88 for (int i = 0; i < objects->length(); i++) { 5.89 assert(objects->at(i)->is_object(), "invalid debug information"); 5.90 ObjectValue* sv = (ObjectValue*) objects->at(i); 5.91 @@ -762,27 +773,34 @@ 5.92 5.93 if (k->oop_is_instance()) { 5.94 InstanceKlass* ik = InstanceKlass::cast(k()); 5.95 - obj = ik->allocate_instance(CHECK_(false)); 5.96 + obj = ik->allocate_instance(THREAD); 5.97 } else if (k->oop_is_typeArray()) { 5.98 TypeArrayKlass* ak = TypeArrayKlass::cast(k()); 5.99 assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length"); 5.100 int len = sv->field_size() / type2size[ak->element_type()]; 5.101 - obj = ak->allocate(len, CHECK_(false)); 5.102 + obj = ak->allocate(len, THREAD); 5.103 } else if (k->oop_is_objArray()) { 5.104 ObjArrayKlass* ak = ObjArrayKlass::cast(k()); 5.105 - obj = ak->allocate(sv->field_size(), CHECK_(false)); 5.106 + obj = ak->allocate(sv->field_size(), THREAD); 5.107 } 5.108 5.109 - assert(obj != NULL, "allocation failed"); 5.110 + if (obj == NULL) { 5.111 + failures = true; 5.112 + } 5.113 + 5.114 assert(sv->value().is_null(), "redundant reallocation"); 5.115 + assert(obj != NULL || HAS_PENDING_EXCEPTION, "allocation should succeed or we should get an exception"); 5.116 + CLEAR_PENDING_EXCEPTION; 5.117 sv->set_value(obj); 5.118 } 5.119 5.120 - if (pending_exception.not_null()) { 5.121 + if (failures) { 5.122 + THROW_OOP_(Universe::out_of_memory_error_realloc_objects(), failures); 5.123 + } else if (pending_exception.not_null()) { 5.124 thread->set_pending_exception(pending_exception(), exception_file, exception_line); 5.125 } 5.126 5.127 - return true; 5.128 + return failures; 5.129 } 5.130 5.131 // This assumes that the fields are stored in ObjectValue in the same order 5.132 @@ -920,12 +938,15 @@ 5.133 5.134 5.135 // restore fields of all eliminated objects and arrays 5.136 -void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects) { 5.137 +void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures) { 5.138 for (int i = 0; i < objects->length(); i++) { 5.139 ObjectValue* sv = (ObjectValue*) objects->at(i); 5.140 KlassHandle k(java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()())); 5.141 Handle obj = sv->value(); 5.142 - assert(obj.not_null(), "reallocation was missed"); 5.143 + assert(obj.not_null() || realloc_failures, "reallocation was missed"); 5.144 + if (obj.is_null()) { 5.145 + continue; 5.146 + } 5.147 5.148 if (k->oop_is_instance()) { 5.149 InstanceKlass* ik = InstanceKlass::cast(k()); 5.150 @@ -942,34 +963,36 @@ 5.151 5.152 5.153 // relock objects for which synchronization was eliminated 5.154 -void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread) { 5.155 +void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread, bool realloc_failures) { 5.156 for (int i = 0; i < monitors->length(); i++) { 5.157 MonitorInfo* mon_info = monitors->at(i); 5.158 if (mon_info->eliminated()) { 5.159 - assert(mon_info->owner() != NULL, "reallocation was missed"); 5.160 - Handle obj = Handle(mon_info->owner()); 5.161 - markOop mark = obj->mark(); 5.162 - if (UseBiasedLocking && mark->has_bias_pattern()) { 5.163 - // New allocated objects may have the mark set to anonymously biased. 5.164 - // Also the deoptimized method may called methods with synchronization 5.165 - // where the thread-local object is bias locked to the current thread. 5.166 - assert(mark->is_biased_anonymously() || 5.167 - mark->biased_locker() == thread, "should be locked to current thread"); 5.168 - // Reset mark word to unbiased prototype. 5.169 - markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); 5.170 - obj->set_mark(unbiased_prototype); 5.171 + assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed"); 5.172 + if (!mon_info->owner_is_scalar_replaced()) { 5.173 + Handle obj = Handle(mon_info->owner()); 5.174 + markOop mark = obj->mark(); 5.175 + if (UseBiasedLocking && mark->has_bias_pattern()) { 5.176 + // New allocated objects may have the mark set to anonymously biased. 5.177 + // Also the deoptimized method may called methods with synchronization 5.178 + // where the thread-local object is bias locked to the current thread. 5.179 + assert(mark->is_biased_anonymously() || 5.180 + mark->biased_locker() == thread, "should be locked to current thread"); 5.181 + // Reset mark word to unbiased prototype. 5.182 + markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); 5.183 + obj->set_mark(unbiased_prototype); 5.184 + } 5.185 + BasicLock* lock = mon_info->lock(); 5.186 + ObjectSynchronizer::slow_enter(obj, lock, thread); 5.187 + assert(mon_info->owner()->is_locked(), "object must be locked now"); 5.188 } 5.189 - BasicLock* lock = mon_info->lock(); 5.190 - ObjectSynchronizer::slow_enter(obj, lock, thread); 5.191 } 5.192 - assert(mon_info->owner()->is_locked(), "object must be locked now"); 5.193 } 5.194 } 5.195 5.196 5.197 #ifndef PRODUCT 5.198 // print information about reallocated objects 5.199 -void Deoptimization::print_objects(GrowableArray<ScopeValue*>* objects) { 5.200 +void Deoptimization::print_objects(GrowableArray<ScopeValue*>* objects, bool realloc_failures) { 5.201 fieldDescriptor fd; 5.202 5.203 for (int i = 0; i < objects->length(); i++) { 5.204 @@ -979,10 +1002,15 @@ 5.205 5.206 tty->print(" object <" INTPTR_FORMAT "> of type ", (void *)sv->value()()); 5.207 k->print_value(); 5.208 - tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); 5.209 + assert(obj.not_null() || realloc_failures, "reallocation was missed"); 5.210 + if (obj.is_null()) { 5.211 + tty->print(" allocation failed"); 5.212 + } else { 5.213 + tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); 5.214 + } 5.215 tty->cr(); 5.216 5.217 - if (Verbose) { 5.218 + if (Verbose && !obj.is_null()) { 5.219 k->oop_print_on(obj(), tty); 5.220 } 5.221 } 5.222 @@ -990,7 +1018,7 @@ 5.223 #endif 5.224 #endif // COMPILER2 5.225 5.226 -vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray<compiledVFrame*>* chunk) { 5.227 +vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures) { 5.228 Events::log(thread, "DEOPT PACKING pc=" INTPTR_FORMAT " sp=" INTPTR_FORMAT, fr.pc(), fr.sp()); 5.229 5.230 #ifndef PRODUCT 5.231 @@ -1033,7 +1061,7 @@ 5.232 // Since the Java thread being deoptimized will eventually adjust it's own stack, 5.233 // the vframeArray containing the unpacking information is allocated in the C heap. 5.234 // For Compiler1, the caller of the deoptimized frame is saved for use by unpack_frames(). 5.235 - vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr); 5.236 + vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr, realloc_failures); 5.237 5.238 // Compare the vframeArray to the collected vframes 5.239 assert(array->structural_compare(thread, chunk), "just checking"); 5.240 @@ -1048,6 +1076,33 @@ 5.241 return array; 5.242 } 5.243 5.244 +#ifdef COMPILER2 5.245 +void Deoptimization::pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array) { 5.246 + // Reallocation of some scalar replaced objects failed. Record 5.247 + // that we need to pop all the interpreter frames for the 5.248 + // deoptimized compiled frame. 5.249 + assert(thread->frames_to_pop_failed_realloc() == 0, "missed frames to pop?"); 5.250 + thread->set_frames_to_pop_failed_realloc(array->frames()); 5.251 + // Unlock all monitors here otherwise the interpreter will see a 5.252 + // mix of locked and unlocked monitors (because of failed 5.253 + // reallocations of synchronized objects) and be confused. 5.254 + for (int i = 0; i < array->frames(); i++) { 5.255 + MonitorChunk* monitors = array->element(i)->monitors(); 5.256 + if (monitors != NULL) { 5.257 + for (int j = 0; j < monitors->number_of_monitors(); j++) { 5.258 + BasicObjectLock* src = monitors->at(j); 5.259 + if (src->obj() != NULL) { 5.260 + ObjectSynchronizer::fast_exit(src->obj(), src->lock(), thread); 5.261 + } 5.262 + } 5.263 + array->element(i)->free_monitors(thread); 5.264 +#ifdef ASSERT 5.265 + array->element(i)->set_removed_monitors(); 5.266 +#endif 5.267 + } 5.268 + } 5.269 +} 5.270 +#endif 5.271 5.272 static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects_to_revoke) { 5.273 GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
6.1 --- a/src/share/vm/runtime/deoptimization.hpp Thu Dec 11 10:38:17 2014 +0000 6.2 +++ b/src/share/vm/runtime/deoptimization.hpp Tue Nov 25 17:33:59 2014 +0100 6.3 @@ -120,13 +120,14 @@ 6.4 static bool realloc_objects(JavaThread* thread, frame* fr, GrowableArray<ScopeValue*>* objects, TRAPS); 6.5 static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type); 6.6 static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj); 6.7 - static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects); 6.8 - static void relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread); 6.9 - NOT_PRODUCT(static void print_objects(GrowableArray<ScopeValue*>* objects);) 6.10 + static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects, bool realloc_failures); 6.11 + static void relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread, bool realloc_failures); 6.12 + static void pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array); 6.13 + NOT_PRODUCT(static void print_objects(GrowableArray<ScopeValue*>* objects, bool realloc_failures);) 6.14 #endif // COMPILER2 6.15 6.16 public: 6.17 - static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray<compiledVFrame*>* chunk); 6.18 + static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures); 6.19 6.20 // Interface used for unpacking deoptimized frames 6.21
7.1 --- a/src/share/vm/runtime/sharedRuntime.cpp Thu Dec 11 10:38:17 2014 +0000 7.2 +++ b/src/share/vm/runtime/sharedRuntime.cpp Tue Nov 25 17:33:59 2014 +0100 7.3 @@ -482,6 +482,7 @@ 7.4 7.5 address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* thread, address return_address) { 7.6 assert(frame::verify_return_pc(return_address), err_msg("must be a return address: " INTPTR_FORMAT, return_address)); 7.7 + assert(thread->frames_to_pop_failed_realloc() == 0 || Interpreter::contains(return_address), "missed frames to pop?"); 7.8 7.9 // Reset method handle flag. 7.10 thread->set_is_method_handle_return(false);
8.1 --- a/src/share/vm/runtime/thread.cpp Thu Dec 11 10:38:17 2014 +0000 8.2 +++ b/src/share/vm/runtime/thread.cpp Tue Nov 25 17:33:59 2014 +0100 8.3 @@ -1495,6 +1495,7 @@ 8.4 _popframe_condition = popframe_inactive; 8.5 _popframe_preserved_args = NULL; 8.6 _popframe_preserved_args_size = 0; 8.7 + _frames_to_pop_failed_realloc = 0; 8.8 8.9 pd_initialize(); 8.10 }
9.1 --- a/src/share/vm/runtime/thread.hpp Thu Dec 11 10:38:17 2014 +0000 9.2 +++ b/src/share/vm/runtime/thread.hpp Tue Nov 25 17:33:59 2014 +0100 9.3 @@ -933,6 +933,12 @@ 9.4 // This is set to popframe_pending to signal that top Java frame should be popped immediately 9.5 int _popframe_condition; 9.6 9.7 + // If reallocation of scalar replaced objects fails, we throw OOM 9.8 + // and during exception propagation, pop the top 9.9 + // _frames_to_pop_failed_realloc frames, the ones that reference 9.10 + // failed reallocations. 9.11 + int _frames_to_pop_failed_realloc; 9.12 + 9.13 #ifndef PRODUCT 9.14 int _jmp_ring_index; 9.15 struct { 9.16 @@ -1585,6 +1591,10 @@ 9.17 void clr_pop_frame_in_process(void) { _popframe_condition &= ~popframe_processing_bit; } 9.18 #endif 9.19 9.20 + int frames_to_pop_failed_realloc() const { return _frames_to_pop_failed_realloc; } 9.21 + void set_frames_to_pop_failed_realloc(int nb) { _frames_to_pop_failed_realloc = nb; } 9.22 + void dec_frames_to_pop_failed_realloc() { _frames_to_pop_failed_realloc--; } 9.23 + 9.24 private: 9.25 // Saved incoming arguments to popped frame. 9.26 // Used only when popped interpreted frame returns to deoptimized frame.
10.1 --- a/src/share/vm/runtime/vframeArray.cpp Thu Dec 11 10:38:17 2014 +0000 10.2 +++ b/src/share/vm/runtime/vframeArray.cpp Tue Nov 25 17:33:59 2014 +0100 10.3 @@ -56,7 +56,7 @@ 10.4 } 10.5 } 10.6 10.7 -void vframeArrayElement::fill_in(compiledVFrame* vf) { 10.8 +void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { 10.9 10.10 // Copy the information from the compiled vframe to the 10.11 // interpreter frame we will be creating to replace vf 10.12 @@ -64,6 +64,9 @@ 10.13 _method = vf->method(); 10.14 _bci = vf->raw_bci(); 10.15 _reexecute = vf->should_reexecute(); 10.16 +#ifdef ASSERT 10.17 + _removed_monitors = false; 10.18 +#endif 10.19 10.20 int index; 10.21 10.22 @@ -81,11 +84,15 @@ 10.23 // Migrate the BasicLocks from the stack to the monitor chunk 10.24 for (index = 0; index < list->length(); index++) { 10.25 MonitorInfo* monitor = list->at(index); 10.26 - assert(!monitor->owner_is_scalar_replaced(), "object should be reallocated already"); 10.27 - assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); 10.28 + assert(!monitor->owner_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); 10.29 BasicObjectLock* dest = _monitors->at(index); 10.30 - dest->set_obj(monitor->owner()); 10.31 - monitor->lock()->move_to(monitor->owner(), dest->lock()); 10.32 + if (monitor->owner_is_scalar_replaced()) { 10.33 + dest->set_obj(NULL); 10.34 + } else { 10.35 + assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); 10.36 + dest->set_obj(monitor->owner()); 10.37 + monitor->lock()->move_to(monitor->owner(), dest->lock()); 10.38 + } 10.39 } 10.40 } 10.41 10.42 @@ -110,7 +117,7 @@ 10.43 StackValue* value = locs->at(index); 10.44 switch(value->type()) { 10.45 case T_OBJECT: 10.46 - assert(!value->obj_is_scalar_replaced(), "object should be reallocated already"); 10.47 + assert(!value->obj_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); 10.48 // preserve object type 10.49 _locals->add( new StackValue(cast_from_oop<intptr_t>((value->get_obj()())), T_OBJECT )); 10.50 break; 10.51 @@ -135,7 +142,7 @@ 10.52 StackValue* value = exprs->at(index); 10.53 switch(value->type()) { 10.54 case T_OBJECT: 10.55 - assert(!value->obj_is_scalar_replaced(), "object should be reallocated already"); 10.56 + assert(!value->obj_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); 10.57 // preserve object type 10.58 _expressions->add( new StackValue(cast_from_oop<intptr_t>((value->get_obj()())), T_OBJECT )); 10.59 break; 10.60 @@ -286,7 +293,7 @@ 10.61 10.62 _frame.patch_pc(thread, pc); 10.63 10.64 - assert (!method()->is_synchronized() || locks > 0, "synchronized methods must have monitors"); 10.65 + assert (!method()->is_synchronized() || locks > 0 || _removed_monitors, "synchronized methods must have monitors"); 10.66 10.67 BasicObjectLock* top = iframe()->interpreter_frame_monitor_begin(); 10.68 for (int index = 0; index < locks; index++) { 10.69 @@ -438,7 +445,8 @@ 10.70 10.71 10.72 vframeArray* vframeArray::allocate(JavaThread* thread, int frame_size, GrowableArray<compiledVFrame*>* chunk, 10.73 - RegisterMap *reg_map, frame sender, frame caller, frame self) { 10.74 + RegisterMap *reg_map, frame sender, frame caller, frame self, 10.75 + bool realloc_failures) { 10.76 10.77 // Allocate the vframeArray 10.78 vframeArray * result = (vframeArray*) AllocateHeap(sizeof(vframeArray) + // fixed part 10.79 @@ -450,19 +458,20 @@ 10.80 result->_caller = caller; 10.81 result->_original = self; 10.82 result->set_unroll_block(NULL); // initialize it 10.83 - result->fill_in(thread, frame_size, chunk, reg_map); 10.84 + result->fill_in(thread, frame_size, chunk, reg_map, realloc_failures); 10.85 return result; 10.86 } 10.87 10.88 void vframeArray::fill_in(JavaThread* thread, 10.89 int frame_size, 10.90 GrowableArray<compiledVFrame*>* chunk, 10.91 - const RegisterMap *reg_map) { 10.92 + const RegisterMap *reg_map, 10.93 + bool realloc_failures) { 10.94 // Set owner first, it is used when adding monitor chunks 10.95 10.96 _frame_size = frame_size; 10.97 for(int i = 0; i < chunk->length(); i++) { 10.98 - element(i)->fill_in(chunk->at(i)); 10.99 + element(i)->fill_in(chunk->at(i), realloc_failures); 10.100 } 10.101 10.102 // Copy registers for callee-saved registers
11.1 --- a/src/share/vm/runtime/vframeArray.hpp Thu Dec 11 10:38:17 2014 +0000 11.2 +++ b/src/share/vm/runtime/vframeArray.hpp Tue Nov 25 17:33:59 2014 +0100 11.3 @@ -58,6 +58,9 @@ 11.4 MonitorChunk* _monitors; // active monitors for this vframe 11.5 StackValueCollection* _locals; 11.6 StackValueCollection* _expressions; 11.7 +#ifdef ASSERT 11.8 + bool _removed_monitors; 11.9 +#endif 11.10 11.11 public: 11.12 11.13 @@ -78,7 +81,7 @@ 11.14 11.15 StackValueCollection* expressions(void) const { return _expressions; } 11.16 11.17 - void fill_in(compiledVFrame* vf); 11.18 + void fill_in(compiledVFrame* vf, bool realloc_failures); 11.19 11.20 // Formerly part of deoptimizedVFrame 11.21 11.22 @@ -99,6 +102,12 @@ 11.23 bool is_bottom_frame, 11.24 int exec_mode); 11.25 11.26 +#ifdef ASSERT 11.27 + void set_removed_monitors() { 11.28 + _removed_monitors = true; 11.29 + } 11.30 +#endif 11.31 + 11.32 #ifndef PRODUCT 11.33 void print(outputStream* st); 11.34 #endif /* PRODUCT */ 11.35 @@ -160,13 +169,14 @@ 11.36 int frames() const { return _frames; } 11.37 11.38 static vframeArray* allocate(JavaThread* thread, int frame_size, GrowableArray<compiledVFrame*>* chunk, 11.39 - RegisterMap* reg_map, frame sender, frame caller, frame self); 11.40 + RegisterMap* reg_map, frame sender, frame caller, frame self, 11.41 + bool realloc_failures); 11.42 11.43 11.44 vframeArrayElement* element(int index) { assert(is_within_bounds(index), "Bad index"); return &_elements[index]; } 11.45 11.46 // Allocates a new vframe in the array and fills the array with vframe information in chunk 11.47 - void fill_in(JavaThread* thread, int frame_size, GrowableArray<compiledVFrame*>* chunk, const RegisterMap *reg_map); 11.48 + void fill_in(JavaThread* thread, int frame_size, GrowableArray<compiledVFrame*>* chunk, const RegisterMap *reg_map, bool realloc_failures); 11.49 11.50 // Returns the owner of this vframeArray 11.51 JavaThread* owner_thread() const { return _owner_thread; }
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/test/compiler/uncommontrap/TestDeoptOOM.java Tue Nov 25 17:33:59 2014 +0100 12.3 @@ -0,0 +1,426 @@ 12.4 +/* 12.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 12.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 12.7 + * 12.8 + * This code is free software; you can redistribute it and/or modify it 12.9 + * under the terms of the GNU General Public License version 2 only, as 12.10 + * published by the Free Software Foundation. 12.11 + * 12.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 12.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12.15 + * version 2 for more details (a copy is included in the LICENSE file that 12.16 + * accompanied this code). 12.17 + * 12.18 + * You should have received a copy of the GNU General Public License version 12.19 + * 2 along with this work; if not, write to the Free Software Foundation, 12.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 12.21 + * 12.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 12.23 + * or visit www.oracle.com if you need additional information or have any 12.24 + * questions. 12.25 + */ 12.26 + 12.27 +/* 12.28 + * @test 12.29 + * @bug 6898462 12.30 + * @summary failed reallocations of scalar replaced objects during deoptimization causes crash 12.31 + * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=exclude,TestDeoptOOM::main -XX:CompileCommand=exclude,TestDeoptOOM::m9_1 -Xmx128M TestDeoptOOM 12.32 + * 12.33 + */ 12.34 + 12.35 +public class TestDeoptOOM { 12.36 + 12.37 + long f1; 12.38 + long f2; 12.39 + long f3; 12.40 + long f4; 12.41 + long f5; 12.42 + 12.43 + static class LinkedList { 12.44 + LinkedList l; 12.45 + long[] array; 12.46 + LinkedList(LinkedList l, int size) { 12.47 + array = new long[size]; 12.48 + this.l = l; 12.49 + } 12.50 + } 12.51 + 12.52 + static LinkedList ll; 12.53 + 12.54 + static void consume_all_memory() { 12.55 + int size = 128 * 1024 * 1024; 12.56 + while(size > 0) { 12.57 + try { 12.58 + while(true) { 12.59 + ll = new LinkedList(ll, size); 12.60 + } 12.61 + } catch(OutOfMemoryError oom) { 12.62 + } 12.63 + size = size / 2; 12.64 + } 12.65 + } 12.66 + 12.67 + static void free_memory() { 12.68 + ll = null; 12.69 + } 12.70 + 12.71 + static TestDeoptOOM m1(boolean deopt) { 12.72 + try { 12.73 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.74 + if (deopt) { 12.75 + return tdoom; 12.76 + } 12.77 + } catch(OutOfMemoryError oom) { 12.78 + free_memory(); 12.79 + System.out.println("OOM caught in m1"); 12.80 + } 12.81 + return null; 12.82 + } 12.83 + 12.84 + static TestDeoptOOM m2_1(boolean deopt) { 12.85 + try { 12.86 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.87 + if (deopt) { 12.88 + return tdoom; 12.89 + } 12.90 + } catch(OutOfMemoryError oom) { 12.91 + free_memory(); 12.92 + System.out.println("OOM caught in m2_1"); 12.93 + } 12.94 + return null; 12.95 + } 12.96 + 12.97 + static TestDeoptOOM m2(boolean deopt) { 12.98 + try { 12.99 + return m2_1(deopt); 12.100 + } catch(OutOfMemoryError oom) { 12.101 + free_memory(); 12.102 + System.out.println("OOM caught in m2"); 12.103 + } 12.104 + return null; 12.105 + } 12.106 + 12.107 + static TestDeoptOOM m3_3(boolean deopt) { 12.108 + try { 12.109 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.110 + if (deopt) { 12.111 + return tdoom; 12.112 + } 12.113 + } catch(OutOfMemoryError oom) { 12.114 + free_memory(); 12.115 + System.out.println("OOM caught in m3_3"); 12.116 + } 12.117 + return null; 12.118 + } 12.119 + 12.120 + static boolean m3_2(boolean deopt) { 12.121 + try { 12.122 + return m3_3(deopt) != null; 12.123 + } catch(OutOfMemoryError oom) { 12.124 + free_memory(); 12.125 + System.out.println("OOM caught in m3_2"); 12.126 + } 12.127 + return false; 12.128 + } 12.129 + 12.130 + static TestDeoptOOM m3_1(boolean deopt) { 12.131 + try { 12.132 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.133 + if (m3_2(deopt)) { 12.134 + return tdoom; 12.135 + } 12.136 + } catch(OutOfMemoryError oom) { 12.137 + free_memory(); 12.138 + System.out.println("OOM caught in m3_1"); 12.139 + } 12.140 + return null; 12.141 + } 12.142 + 12.143 + static TestDeoptOOM m3(boolean deopt) { 12.144 + try { 12.145 + return m3_1(deopt); 12.146 + } catch(OutOfMemoryError oom) { 12.147 + free_memory(); 12.148 + System.out.println("OOM caught in m3"); 12.149 + } 12.150 + return null; 12.151 + } 12.152 + 12.153 + static TestDeoptOOM m4(boolean deopt) { 12.154 + try { 12.155 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.156 + if (deopt) { 12.157 + tdoom.f1 = 1l; 12.158 + tdoom.f2 = 2l; 12.159 + tdoom.f3 = 3l; 12.160 + return tdoom; 12.161 + } 12.162 + } catch(OutOfMemoryError oom) { 12.163 + free_memory(); 12.164 + System.out.println("OOM caught in m4"); 12.165 + } 12.166 + return null; 12.167 + } 12.168 + 12.169 + static TestDeoptOOM m5(boolean deopt) { 12.170 + try { 12.171 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.172 + synchronized(tdoom) { 12.173 + if (deopt) { 12.174 + return tdoom; 12.175 + } 12.176 + } 12.177 + } catch(OutOfMemoryError oom) { 12.178 + free_memory(); 12.179 + System.out.println("OOM caught in m5"); 12.180 + } 12.181 + return null; 12.182 + } 12.183 + 12.184 + synchronized TestDeoptOOM m6_1(boolean deopt) { 12.185 + if (deopt) { 12.186 + return this; 12.187 + } 12.188 + return null; 12.189 + } 12.190 + 12.191 + static TestDeoptOOM m6(boolean deopt) { 12.192 + try { 12.193 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.194 + return tdoom.m6_1(deopt); 12.195 + } catch(OutOfMemoryError oom) { 12.196 + free_memory(); 12.197 + System.out.println("OOM caught in m6"); 12.198 + } 12.199 + return null; 12.200 + } 12.201 + 12.202 + static TestDeoptOOM m7_1(boolean deopt, Object lock) { 12.203 + try { 12.204 + synchronized(lock) { 12.205 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.206 + if (deopt) { 12.207 + return tdoom; 12.208 + } 12.209 + } 12.210 + } catch(OutOfMemoryError oom) { 12.211 + free_memory(); 12.212 + System.out.println("OOM caught in m7_1"); 12.213 + } 12.214 + return null; 12.215 + } 12.216 + 12.217 + static TestDeoptOOM m7(boolean deopt, Object lock) { 12.218 + try { 12.219 + return m7_1(deopt, lock); 12.220 + } catch(OutOfMemoryError oom) { 12.221 + free_memory(); 12.222 + System.out.println("OOM caught in m7"); 12.223 + } 12.224 + return null; 12.225 + } 12.226 + 12.227 + static class A { 12.228 + long f1; 12.229 + long f2; 12.230 + long f3; 12.231 + long f4; 12.232 + long f5; 12.233 + } 12.234 + 12.235 + static class B { 12.236 + long f1; 12.237 + long f2; 12.238 + long f3; 12.239 + long f4; 12.240 + long f5; 12.241 + 12.242 + A a; 12.243 + } 12.244 + 12.245 + static B m8(boolean deopt) { 12.246 + try { 12.247 + A a = new A(); 12.248 + B b = new B(); 12.249 + b.a = a; 12.250 + if (deopt) { 12.251 + return b; 12.252 + } 12.253 + } catch(OutOfMemoryError oom) { 12.254 + free_memory(); 12.255 + System.out.println("OOM caught in m8"); 12.256 + } 12.257 + return null; 12.258 + } 12.259 + 12.260 + static void m9_1(int i) { 12.261 + if (i > 90000) { 12.262 + consume_all_memory(); 12.263 + } 12.264 + } 12.265 + 12.266 + static TestDeoptOOM m9() { 12.267 + try { 12.268 + for (int i = 0; i < 100000; i++) { 12.269 + TestDeoptOOM tdoom = new TestDeoptOOM(); 12.270 + m9_1(i); 12.271 + if (i > 90000) { 12.272 + return tdoom; 12.273 + } 12.274 + } 12.275 + } catch(OutOfMemoryError oom) { 12.276 + free_memory(); 12.277 + System.out.println("OOM caught in m1"); 12.278 + } 12.279 + return null; 12.280 + } 12.281 + 12.282 + public static void main(String[] args) { 12.283 + for (int i = 0; i < 20000; i++) { 12.284 + m1(false); 12.285 + } 12.286 + 12.287 + consume_all_memory(); 12.288 + 12.289 + try { 12.290 + m1(true); 12.291 + } catch(OutOfMemoryError oom) { 12.292 + free_memory(); 12.293 + System.out.println("OOM caught in main " + oom.getMessage()); 12.294 + } 12.295 + 12.296 + free_memory(); 12.297 + 12.298 + for (int i = 0; i < 20000; i++) { 12.299 + m2(false); 12.300 + } 12.301 + 12.302 + consume_all_memory(); 12.303 + 12.304 + try { 12.305 + m2(true); 12.306 + } catch(OutOfMemoryError oom) { 12.307 + free_memory(); 12.308 + System.out.println("OOM caught in main"); 12.309 + } 12.310 + 12.311 + free_memory(); 12.312 + 12.313 + for (int i = 0; i < 20000; i++) { 12.314 + m3(false); 12.315 + } 12.316 + 12.317 + consume_all_memory(); 12.318 + 12.319 + try { 12.320 + m3(true); 12.321 + } catch(OutOfMemoryError oom) { 12.322 + free_memory(); 12.323 + System.out.println("OOM caught in main"); 12.324 + } 12.325 + 12.326 + free_memory(); 12.327 + 12.328 + for (int i = 0; i < 20000; i++) { 12.329 + m4(false); 12.330 + } 12.331 + 12.332 + consume_all_memory(); 12.333 + 12.334 + try { 12.335 + m4(true); 12.336 + } catch(OutOfMemoryError oom) { 12.337 + free_memory(); 12.338 + System.out.println("OOM caught in main"); 12.339 + } 12.340 + 12.341 + free_memory(); 12.342 + 12.343 + for (int i = 0; i < 20000; i++) { 12.344 + m5(false); 12.345 + } 12.346 + 12.347 + consume_all_memory(); 12.348 + 12.349 + try { 12.350 + m5(true); 12.351 + } catch(OutOfMemoryError oom) { 12.352 + free_memory(); 12.353 + System.out.println("OOM caught in main"); 12.354 + } 12.355 + 12.356 + free_memory(); 12.357 + 12.358 + for (int i = 0; i < 20000; i++) { 12.359 + m6(false); 12.360 + } 12.361 + 12.362 + consume_all_memory(); 12.363 + 12.364 + try { 12.365 + m6(true); 12.366 + } catch(OutOfMemoryError oom) { 12.367 + free_memory(); 12.368 + System.out.println("OOM caught in main"); 12.369 + } 12.370 + 12.371 + free_memory(); 12.372 + 12.373 + final Object lock = new Object(); 12.374 + 12.375 + for (int i = 0; i < 20000; i++) { 12.376 + m7(false, lock); 12.377 + } 12.378 + 12.379 + consume_all_memory(); 12.380 + 12.381 + try { 12.382 + m7(true, lock); 12.383 + } catch(OutOfMemoryError oom) { 12.384 + free_memory(); 12.385 + System.out.println("OOM caught in main"); 12.386 + } 12.387 + 12.388 + free_memory(); 12.389 + 12.390 + Thread thread = new Thread() { 12.391 + public void run() { 12.392 + System.out.println("Acquiring lock"); 12.393 + synchronized(lock) { 12.394 + System.out.println("Lock acquired"); 12.395 + } 12.396 + System.out.println("Lock released"); 12.397 + } 12.398 + }; 12.399 + thread.start(); 12.400 + try { 12.401 + thread.join(); 12.402 + } catch(InterruptedException ie) { 12.403 + } 12.404 + 12.405 + for (int i = 0; i < 20000; i++) { 12.406 + m8(false); 12.407 + } 12.408 + 12.409 + consume_all_memory(); 12.410 + 12.411 + try { 12.412 + m8(true); 12.413 + } catch(OutOfMemoryError oom) { 12.414 + free_memory(); 12.415 + System.out.println("OOM caught in main"); 12.416 + } 12.417 + 12.418 + free_memory(); 12.419 + 12.420 + try { 12.421 + m9(); 12.422 + } catch(OutOfMemoryError oom) { 12.423 + free_memory(); 12.424 + System.out.println("OOM caught in main"); 12.425 + } 12.426 + 12.427 + free_memory(); 12.428 + } 12.429 +}