Skip to content

Commit 4c3efb3

Browse files
author
David Holmes
committed
8309034: NoClassDefFoundError when initializing Long$LongCache
Reviewed-by: coleenp, fparain
1 parent 1120106 commit 4c3efb3

File tree

6 files changed

+207
-8
lines changed

6 files changed

+207
-8
lines changed

src/hotspot/share/classfile/javaClasses.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2777,7 +2777,7 @@ Handle java_lang_Throwable::create_initialization_error(JavaThread* current, Han
27772777
// If new_exception returns a different exception while creating the exception,
27782778
// abandon the attempt to save the initialization error and return null.
27792779
if (init_error->klass()->name() != exception_name) {
2780-
log_info(class, init)("Exception thrown while saving initialization exception %s",
2780+
log_info(class, init)("Exception %s thrown while saving initialization exception",
27812781
init_error->klass()->external_name());
27822782
return Handle();
27832783
}

src/hotspot/share/memory/universe.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ enum OutOfMemoryInstance { _oom_java_heap,
114114
_oom_count };
115115

116116
OopHandle Universe::_out_of_memory_errors;
117+
OopHandle Universe:: _class_init_stack_overflow_error;
117118
OopHandle Universe::_delayed_stack_overflow_error_message;
118119
OopHandle Universe::_preallocated_out_of_memory_error_array;
119120
volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0;
@@ -610,6 +611,9 @@ oop Universe::out_of_memory_error_realloc_objects() {
610611

611612
// Throw default _out_of_memory_error_retry object as it will never propagate out of the VM
612613
oop Universe::out_of_memory_error_retry() { return out_of_memory_errors()->obj_at(_oom_retry); }
614+
615+
oop Universe::class_init_out_of_memory_error() { return out_of_memory_errors()->obj_at(_oom_java_heap); }
616+
oop Universe::class_init_stack_overflow_error() { return _class_init_stack_overflow_error.resolve(); }
613617
oop Universe::delayed_stack_overflow_error_message() { return _delayed_stack_overflow_error_message.resolve(); }
614618

615619

@@ -1028,6 +1032,11 @@ bool universe_post_init() {
10281032
Handle msg = java_lang_String::create_from_str("/ by zero", CHECK_false);
10291033
java_lang_Throwable::set_message(Universe::arithmetic_exception_instance(), msg());
10301034

1035+
// Setup preallocated StackOverflowError for use with class initialization failure
1036+
k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_StackOverflowError(), true, CHECK_false);
1037+
instance = InstanceKlass::cast(k)->allocate_instance(CHECK_false);
1038+
Universe::_class_init_stack_overflow_error = OopHandle(Universe::vm_global(), instance);
1039+
10311040
Universe::initialize_known_methods(CHECK_false);
10321041

10331042
// This needs to be done before the first scavenge/gc, since

src/hotspot/share/memory/universe.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class Universe: AllStatic {
110110

111111
// preallocated error objects (no backtrace)
112112
static OopHandle _out_of_memory_errors;
113+
static OopHandle _class_init_stack_overflow_error;
113114

114115
// preallocated cause message for delayed StackOverflowError
115116
static OopHandle _delayed_stack_overflow_error_message;
@@ -313,6 +314,11 @@ class Universe: AllStatic {
313314
static oop out_of_memory_error_retry();
314315
static oop delayed_stack_overflow_error_message();
315316

317+
// Saved StackOverflowError and OutOfMemoryError for use when
318+
// class initialization can't create ExceptionInInitializerError.
319+
static oop class_init_stack_overflow_error();
320+
static oop class_init_out_of_memory_error();
321+
316322
// If it's a certain type of OOME object
317323
static bool is_out_of_memory_error_metaspace(oop ex_obj);
318324
static bool is_out_of_memory_error_class_metaspace(oop ex_obj);

src/hotspot/share/oops/instanceKlass.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -984,17 +984,26 @@ ResourceHashtable<const InstanceKlass*, OopHandle, 107, AnyObj::C_HEAP, mtClass>
984984
void InstanceKlass::add_initialization_error(JavaThread* current, Handle exception) {
985985
// Create the same exception with a message indicating the thread name,
986986
// and the StackTraceElements.
987-
// If the initialization error is OOM, this might not work, but if GC kicks in
988-
// this would be still be helpful.
989-
JavaThread* THREAD = current;
990987
Handle init_error = java_lang_Throwable::create_initialization_error(current, exception);
991-
ResourceMark rm(THREAD);
988+
ResourceMark rm(current);
992989
if (init_error.is_null()) {
993-
log_trace(class, init)("Initialization error is null for class %s", external_name());
994-
return;
990+
log_trace(class, init)("Unable to create the desired initialization error for class %s", external_name());
991+
992+
// We failed to create the new exception, most likely due to either out-of-memory or
993+
// a stackoverflow error. If the original exception was either of those then we save
994+
// the shared, pre-allocated, stackless, instance of that exception.
995+
if (exception->klass() == vmClasses::StackOverflowError_klass()) {
996+
log_debug(class, init)("Using shared StackOverflowError as initialization error for class %s", external_name());
997+
init_error = Handle(current, Universe::class_init_stack_overflow_error());
998+
} else if (exception->klass() == vmClasses::OutOfMemoryError_klass()) {
999+
log_debug(class, init)("Using shared OutOfMemoryError as initialization error for class %s", external_name());
1000+
init_error = Handle(current, Universe::class_init_out_of_memory_error());
1001+
} else {
1002+
return;
1003+
}
9951004
}
9961005

997-
MutexLocker ml(THREAD, ClassInitError_lock);
1006+
MutexLocker ml(current, ClassInitError_lock);
9981007
OopHandle elem = OopHandle(Universe::vm_global(), init_error());
9991008
bool created;
10001009
_initialization_error_table.put_if_absent(this, elem, &created);
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8309034
27+
* @summary Test that when saving a class initialization failure caused by
28+
* an OutOfMemoryError, that we record the OOME as the underlying
29+
* cause, even if we can't create the ExceptionInInitializerError
30+
*
31+
* @comment Enable logging to ease failure diagnosis
32+
* @run main/othervm -Xms64m -Xmx64m TestOutOfMemoryDuringInit
33+
*/
34+
35+
import java.io.ByteArrayOutputStream;
36+
import java.io.PrintStream;
37+
import java.util.LinkedList;
38+
39+
public class TestOutOfMemoryDuringInit {
40+
41+
static LinkedList<Object> theList = new LinkedList<>();
42+
43+
static class Nested {
44+
static void forceInit() { }
45+
static {
46+
while (theList != null) {
47+
theList.add(new Object());
48+
}
49+
}
50+
}
51+
52+
public static void main(String[] args) throws Exception {
53+
String expected = "java.lang.NoClassDefFoundError: Could not initialize class TestOutOfMemoryDuringInit$Nested";
54+
// This cause will match either the shared OOME or the EIIE we get
55+
// with some GC's.
56+
String cause = "java.lang.OutOfMemoryError";
57+
58+
try {
59+
Nested.forceInit();
60+
} catch (OutOfMemoryError oome) {
61+
theList = null; // free memory for verification process
62+
System.out.println("Trying to access class Nested ...");
63+
try {
64+
Nested.forceInit();
65+
throw new RuntimeException("NoClassDefFoundError was not thrown");
66+
} catch (NoClassDefFoundError ncdfe) {
67+
verify_stack(ncdfe, expected, cause);
68+
}
69+
}
70+
}
71+
72+
private static void verify_stack(Throwable e, String expected, String cause) throws Exception {
73+
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
74+
try (PrintStream printStream = new PrintStream(byteOS)) {
75+
e.printStackTrace(printStream);
76+
}
77+
String stackTrace = byteOS.toString("ASCII");
78+
System.out.println(stackTrace);
79+
if (!stackTrace.contains(expected) ||
80+
(cause != null && !stackTrace.contains(cause))) {
81+
throw new RuntimeException(expected + " and/or " + cause + " missing from stacktrace");
82+
}
83+
}
84+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8309034
27+
* @summary Test that when saving a class initialization failure caused by
28+
* a StackOverflowError, that we record the SOE as the underlying
29+
* cause, even if we can't create the ExceptionInInitializerError
30+
* @requires os.simpleArch == "x64"
31+
* @comment The reproducer only fails in the desired way on x64.
32+
* @requires vm.flagless
33+
* @comment This test could easily be perturbed so don't allow flag settings.
34+
*
35+
* @run main/othervm TestStackOverflowDuringInit
36+
*/
37+
38+
import java.io.ByteArrayOutputStream;
39+
import java.io.PrintStream;
40+
41+
public class TestStackOverflowDuringInit {
42+
43+
// Test case is fuzzed/obfuscated
44+
45+
public static void main(String[] args) throws Exception {
46+
String expected = "java.lang.NoClassDefFoundError: Could not initialize class java.lang.Long$LongCache";
47+
String cause = "Caused by: java.lang.StackOverflowError";
48+
49+
TestStackOverflowDuringInit i = new TestStackOverflowDuringInit();
50+
try {
51+
i.j();
52+
} catch (Throwable ex) {
53+
// ex.printStackTrace();
54+
verify_stack(ex, expected, cause);
55+
}
56+
}
57+
58+
void j() { ((e) new a()).g = 0; }
59+
60+
private static void verify_stack(Throwable e, String expected, String cause) throws Exception {
61+
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
62+
try (PrintStream printStream = new PrintStream(byteOS)) {
63+
e.printStackTrace(printStream);
64+
}
65+
String stackTrace = byteOS.toString("ASCII");
66+
System.out.println(stackTrace);
67+
if (!stackTrace.contains(expected) ||
68+
(cause != null && !stackTrace.contains(cause))) {
69+
throw new RuntimeException(expected + " and/or " + cause + " missing from stacktrace");
70+
}
71+
}
72+
}
73+
74+
class a {
75+
Boolean b;
76+
{
77+
try {
78+
Long.valueOf(509505376256L);
79+
Boolean c =
80+
true ? new d().b
81+
: 5 != ((e)java.util.HashSet.newHashSet(301758).clone()).f;
82+
} finally {
83+
Long.valueOf(0);
84+
}
85+
}
86+
}
87+
class e extends a {
88+
double g;
89+
int f;
90+
}
91+
class d extends a {}

0 commit comments

Comments
 (0)