Ответы, в которых говорится, что JVM будет предварительно выделять OutOfMemoryErrors
, действительно верны.
В дополнение к проверке этого путем провоцирования ситуации нехватки памяти мы можем просто проверить кучу любой JVM (я использовалнебольшая программа, которая просто спит, запустив ее с помощью Oracle Hotspot JVM из Java 8, обновление 31).
Используя jmap
, мы видим, что, похоже, существует 9 экземпляров OutOfMemoryError (хотя у нас есть многопамять):
> jmap -histo 12103 | grep OutOfMemoryError
71: 9 288 java.lang.OutOfMemoryError
170: 1 32 [Ljava.lang.OutOfMemoryError;
Затем мы можем сгенерировать дамп кучи:
> jmap -dump:format=b,file=heap.hprof 12315
и открыть его с помощью Eclipse Memory Analyzer , где запрос OQL показывает, чтоПохоже, что JVM предварительно выделяет OutOfMemoryErrors
для всех возможных сообщений:
Код для Java 8 Hotspot Hotspot, который фактически выделяет эти , можно найти здесь и выглядит следующим образом (некоторые части опущены):
...
// Setup preallocated OutOfMemoryError errors
k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_OutOfMemoryError(), true, CHECK_false);
k_h = instanceKlassHandle(THREAD, k);
Universe::_out_of_memory_error_java_heap = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_metaspace = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_class_metaspace = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_array_size = k_h->allocate_instance(CHECK_false);
Universe::_out_of_memory_error_gc_overhead_limit =
k_h->allocate_instance(CHECK_false);
...
if (!DumpSharedSpaces) {
// These are the only Java fields that are currently set during shared space dumping.
// We prefer to not handle this generally, so we always reinitialize these detail messages.
Handle msg = java_lang_String::create_from_str("Java heap space", CHECK_false);
java_lang_Throwable::set_message(Universe::_out_of_memory_error_java_heap, msg());
msg = java_lang_String::create_from_str("Metaspace", CHECK_false);
java_lang_Throwable::set_message(Universe::_out_of_memory_error_metaspace, msg());
msg = java_lang_String::create_from_str("Compressed class space", CHECK_false);
java_lang_Throwable::set_message(Universe::_out_of_memory_error_class_metaspace, msg());
msg = java_lang_String::create_from_str("Requested array size exceeds VM limit", CHECK_false);
java_lang_Throwable::set_message(Universe::_out_of_memory_error_array_size, msg());
msg = java_lang_String::create_from_str("GC overhead limit exceeded", CHECK_false);
java_lang_Throwable::set_message(Universe::_out_of_memory_error_gc_overhead_limit, msg());
msg = java_lang_String::create_from_str("/ by zero", CHECK_false);
java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg());
// Setup the array of errors that have preallocated backtrace
k = Universe::_out_of_memory_error_java_heap->klass();
assert(k->name() == vmSymbols::java_lang_OutOfMemoryError(), "should be out of memory error");
k_h = instanceKlassHandle(THREAD, k);
int len = (StackTraceInThrowable) ? (int)PreallocatedOutOfMemoryErrorCount : 0;
Universe::_preallocated_out_of_memory_error_array = oopFactory::new_objArray(k_h(), len, CHECK_false);
for (int i=0; i<len; i++) {
oop err = k_h->allocate_instance(CHECK_false);
Handle err_h = Handle(THREAD, err);
java_lang_Throwable::allocate_backtrace(err_h, CHECK_false);
Universe::preallocated_out_of_memory_errors()->obj_at_put(i, err_h());
}
Universe::_preallocated_out_of_memory_error_avail_count = (jint)len;
}
...
и этот код показывает, что JVM сначала попытается использовать одну из предварительно выделенных ошибок с пробеломдля трассировки стека, а затем Fвсе обратно к одному без следа стека:
oop Universe::gen_out_of_memory_error(oop default_err) {
// generate an out of memory error:
// - if there is a preallocated error with backtrace available then return it wth
// a filled in stack trace.
// - if there are no preallocated errors with backtrace available then return
// an error without backtrace.
int next;
if (_preallocated_out_of_memory_error_avail_count > 0) {
next = (int)Atomic::add(-1, &_preallocated_out_of_memory_error_avail_count);
assert(next < (int)PreallocatedOutOfMemoryErrorCount, "avail count is corrupt");
} else {
next = -1;
}
if (next < 0) {
// all preallocated errors have been used.
// return default
return default_err;
} else {
// get the error object at the slot and set set it to NULL so that the
// array isn't keeping it alive anymore.
oop exc = preallocated_out_of_memory_errors()->obj_at(next);
assert(exc != NULL, "slot has been used already");
preallocated_out_of_memory_errors()->obj_at_put(next, NULL);
// use the message from the default error
oop msg = java_lang_Throwable::message(default_err);
assert(msg != NULL, "no message");
java_lang_Throwable::set_message(exc, msg);
// populate the stack trace and return it.
java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(exc);
return exc;
}
}