8309034: NoClassDefFoundError when initializing Long$LongCache
Reviewed-by: coleenp, fparain
This commit is contained in:
parent
11201067a8
commit
4c3efb3910
@ -2777,7 +2777,7 @@ Handle java_lang_Throwable::create_initialization_error(JavaThread* current, Han
|
||||
// If new_exception returns a different exception while creating the exception,
|
||||
// abandon the attempt to save the initialization error and return null.
|
||||
if (init_error->klass()->name() != exception_name) {
|
||||
log_info(class, init)("Exception thrown while saving initialization exception %s",
|
||||
log_info(class, init)("Exception %s thrown while saving initialization exception",
|
||||
init_error->klass()->external_name());
|
||||
return Handle();
|
||||
}
|
||||
|
@ -114,6 +114,7 @@ enum OutOfMemoryInstance { _oom_java_heap,
|
||||
_oom_count };
|
||||
|
||||
OopHandle Universe::_out_of_memory_errors;
|
||||
OopHandle Universe:: _class_init_stack_overflow_error;
|
||||
OopHandle Universe::_delayed_stack_overflow_error_message;
|
||||
OopHandle Universe::_preallocated_out_of_memory_error_array;
|
||||
volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0;
|
||||
@ -610,6 +611,9 @@ oop Universe::out_of_memory_error_realloc_objects() {
|
||||
|
||||
// Throw default _out_of_memory_error_retry object as it will never propagate out of the VM
|
||||
oop Universe::out_of_memory_error_retry() { return out_of_memory_errors()->obj_at(_oom_retry); }
|
||||
|
||||
oop Universe::class_init_out_of_memory_error() { return out_of_memory_errors()->obj_at(_oom_java_heap); }
|
||||
oop Universe::class_init_stack_overflow_error() { return _class_init_stack_overflow_error.resolve(); }
|
||||
oop Universe::delayed_stack_overflow_error_message() { return _delayed_stack_overflow_error_message.resolve(); }
|
||||
|
||||
|
||||
@ -1028,6 +1032,11 @@ bool universe_post_init() {
|
||||
Handle msg = java_lang_String::create_from_str("/ by zero", CHECK_false);
|
||||
java_lang_Throwable::set_message(Universe::arithmetic_exception_instance(), msg());
|
||||
|
||||
// Setup preallocated StackOverflowError for use with class initialization failure
|
||||
k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_StackOverflowError(), true, CHECK_false);
|
||||
instance = InstanceKlass::cast(k)->allocate_instance(CHECK_false);
|
||||
Universe::_class_init_stack_overflow_error = OopHandle(Universe::vm_global(), instance);
|
||||
|
||||
Universe::initialize_known_methods(CHECK_false);
|
||||
|
||||
// This needs to be done before the first scavenge/gc, since
|
||||
|
@ -110,6 +110,7 @@ class Universe: AllStatic {
|
||||
|
||||
// preallocated error objects (no backtrace)
|
||||
static OopHandle _out_of_memory_errors;
|
||||
static OopHandle _class_init_stack_overflow_error;
|
||||
|
||||
// preallocated cause message for delayed StackOverflowError
|
||||
static OopHandle _delayed_stack_overflow_error_message;
|
||||
@ -313,6 +314,11 @@ class Universe: AllStatic {
|
||||
static oop out_of_memory_error_retry();
|
||||
static oop delayed_stack_overflow_error_message();
|
||||
|
||||
// Saved StackOverflowError and OutOfMemoryError for use when
|
||||
// class initialization can't create ExceptionInInitializerError.
|
||||
static oop class_init_stack_overflow_error();
|
||||
static oop class_init_out_of_memory_error();
|
||||
|
||||
// If it's a certain type of OOME object
|
||||
static bool is_out_of_memory_error_metaspace(oop ex_obj);
|
||||
static bool is_out_of_memory_error_class_metaspace(oop ex_obj);
|
||||
|
@ -984,17 +984,26 @@ ResourceHashtable<const InstanceKlass*, OopHandle, 107, AnyObj::C_HEAP, mtClass>
|
||||
void InstanceKlass::add_initialization_error(JavaThread* current, Handle exception) {
|
||||
// Create the same exception with a message indicating the thread name,
|
||||
// and the StackTraceElements.
|
||||
// If the initialization error is OOM, this might not work, but if GC kicks in
|
||||
// this would be still be helpful.
|
||||
JavaThread* THREAD = current;
|
||||
Handle init_error = java_lang_Throwable::create_initialization_error(current, exception);
|
||||
ResourceMark rm(THREAD);
|
||||
ResourceMark rm(current);
|
||||
if (init_error.is_null()) {
|
||||
log_trace(class, init)("Initialization error is null for class %s", external_name());
|
||||
return;
|
||||
log_trace(class, init)("Unable to create the desired initialization error for class %s", external_name());
|
||||
|
||||
// We failed to create the new exception, most likely due to either out-of-memory or
|
||||
// a stackoverflow error. If the original exception was either of those then we save
|
||||
// the shared, pre-allocated, stackless, instance of that exception.
|
||||
if (exception->klass() == vmClasses::StackOverflowError_klass()) {
|
||||
log_debug(class, init)("Using shared StackOverflowError as initialization error for class %s", external_name());
|
||||
init_error = Handle(current, Universe::class_init_stack_overflow_error());
|
||||
} else if (exception->klass() == vmClasses::OutOfMemoryError_klass()) {
|
||||
log_debug(class, init)("Using shared OutOfMemoryError as initialization error for class %s", external_name());
|
||||
init_error = Handle(current, Universe::class_init_out_of_memory_error());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MutexLocker ml(THREAD, ClassInitError_lock);
|
||||
MutexLocker ml(current, ClassInitError_lock);
|
||||
OopHandle elem = OopHandle(Universe::vm_global(), init_error());
|
||||
bool created;
|
||||
_initialization_error_table.put_if_absent(this, elem, &created);
|
||||
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8309034
|
||||
* @summary Test that when saving a class initialization failure caused by
|
||||
* an OutOfMemoryError, that we record the OOME as the underlying
|
||||
* cause, even if we can't create the ExceptionInInitializerError
|
||||
*
|
||||
* @comment Enable logging to ease failure diagnosis
|
||||
* @run main/othervm -Xms64m -Xmx64m TestOutOfMemoryDuringInit
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class TestOutOfMemoryDuringInit {
|
||||
|
||||
static LinkedList<Object> theList = new LinkedList<>();
|
||||
|
||||
static class Nested {
|
||||
static void forceInit() { }
|
||||
static {
|
||||
while (theList != null) {
|
||||
theList.add(new Object());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String expected = "java.lang.NoClassDefFoundError: Could not initialize class TestOutOfMemoryDuringInit$Nested";
|
||||
// This cause will match either the shared OOME or the EIIE we get
|
||||
// with some GC's.
|
||||
String cause = "java.lang.OutOfMemoryError";
|
||||
|
||||
try {
|
||||
Nested.forceInit();
|
||||
} catch (OutOfMemoryError oome) {
|
||||
theList = null; // free memory for verification process
|
||||
System.out.println("Trying to access class Nested ...");
|
||||
try {
|
||||
Nested.forceInit();
|
||||
throw new RuntimeException("NoClassDefFoundError was not thrown");
|
||||
} catch (NoClassDefFoundError ncdfe) {
|
||||
verify_stack(ncdfe, expected, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void verify_stack(Throwable e, String expected, String cause) throws Exception {
|
||||
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
|
||||
try (PrintStream printStream = new PrintStream(byteOS)) {
|
||||
e.printStackTrace(printStream);
|
||||
}
|
||||
String stackTrace = byteOS.toString("ASCII");
|
||||
System.out.println(stackTrace);
|
||||
if (!stackTrace.contains(expected) ||
|
||||
(cause != null && !stackTrace.contains(cause))) {
|
||||
throw new RuntimeException(expected + " and/or " + cause + " missing from stacktrace");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8309034
|
||||
* @summary Test that when saving a class initialization failure caused by
|
||||
* a StackOverflowError, that we record the SOE as the underlying
|
||||
* cause, even if we can't create the ExceptionInInitializerError
|
||||
* @requires os.simpleArch == "x64"
|
||||
* @comment The reproducer only fails in the desired way on x64.
|
||||
* @requires vm.flagless
|
||||
* @comment This test could easily be perturbed so don't allow flag settings.
|
||||
*
|
||||
* @run main/othervm TestStackOverflowDuringInit
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class TestStackOverflowDuringInit {
|
||||
|
||||
// Test case is fuzzed/obfuscated
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String expected = "java.lang.NoClassDefFoundError: Could not initialize class java.lang.Long$LongCache";
|
||||
String cause = "Caused by: java.lang.StackOverflowError";
|
||||
|
||||
TestStackOverflowDuringInit i = new TestStackOverflowDuringInit();
|
||||
try {
|
||||
i.j();
|
||||
} catch (Throwable ex) {
|
||||
// ex.printStackTrace();
|
||||
verify_stack(ex, expected, cause);
|
||||
}
|
||||
}
|
||||
|
||||
void j() { ((e) new a()).g = 0; }
|
||||
|
||||
private static void verify_stack(Throwable e, String expected, String cause) throws Exception {
|
||||
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
|
||||
try (PrintStream printStream = new PrintStream(byteOS)) {
|
||||
e.printStackTrace(printStream);
|
||||
}
|
||||
String stackTrace = byteOS.toString("ASCII");
|
||||
System.out.println(stackTrace);
|
||||
if (!stackTrace.contains(expected) ||
|
||||
(cause != null && !stackTrace.contains(cause))) {
|
||||
throw new RuntimeException(expected + " and/or " + cause + " missing from stacktrace");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class a {
|
||||
Boolean b;
|
||||
{
|
||||
try {
|
||||
Long.valueOf(509505376256L);
|
||||
Boolean c =
|
||||
true ? new d().b
|
||||
: 5 != ((e)java.util.HashSet.newHashSet(301758).clone()).f;
|
||||
} finally {
|
||||
Long.valueOf(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
class e extends a {
|
||||
double g;
|
||||
int f;
|
||||
}
|
||||
class d extends a {}
|
Loading…
x
Reference in New Issue
Block a user