check for stack overflow on entry to all non-leaf methods

We now check for stack overflow in the JIT build as well as the
interpreted build, throwing a StackOverflowError if the limit
(currently hard-coded to 64KB, but should be easy to make
configurable) is exceeded.
This commit is contained in:
Joel Dice 2010-12-19 15:23:19 -07:00
parent cac2d2cac5
commit d18240cbd6
8 changed files with 152 additions and 25 deletions

@ -30,7 +30,7 @@ const unsigned HeapCapacity = 768 * 1024 * 1024;
// One of the advantages of a bootimage-based build is that reduces // One of the advantages of a bootimage-based build is that reduces
// the overhead of major GCs at runtime since we can avoid scanning // the overhead of major GCs at runtime since we can avoid scanning
// the pre-built heap image entirely. However, this only works if we // the pre-built heap image entirely. However, this only works if we
// can ensure that no part of the heap image (with an exception noted // can ensure that no part of the heap image (with exceptions noted
// below) ever points to runtime-allocated objects. Therefore (most) // below) ever points to runtime-allocated objects. Therefore (most)
// references in the heap image are considered immutable, and any // references in the heap image are considered immutable, and any
// attempt to update them at runtime will cause the process to abort. // attempt to update them at runtime will cause the process to abort.

@ -45,6 +45,7 @@ class BootImage {
Thunk defaultVirtual; Thunk defaultVirtual;
Thunk native; Thunk native;
Thunk aioob; Thunk aioob;
Thunk stackOverflow;
Thunk table; Thunk table;
}; };
@ -75,6 +76,7 @@ class BootImage {
unsigned compileVirtualMethodCall; unsigned compileVirtualMethodCall;
unsigned invokeNativeCall; unsigned invokeNativeCall;
unsigned throwArrayIndexOutOfBoundsCall; unsigned throwArrayIndexOutOfBoundsCall;
unsigned throwStackOverflowCall;
#define THUNK(s) unsigned s##Call; #define THUNK(s) unsigned s##Call;
#include "thunks.cpp" #include "thunks.cpp"

@ -225,7 +225,8 @@ class MyThread: public Thread {
? parent->arch ? parent->arch
: makeArchitecture(m->system, useNativeFeatures)), : makeArchitecture(m->system, useNativeFeatures)),
transition(0), transition(0),
traceContext(0) traceContext(0),
stackLimit(0)
{ {
arch->acquire(); arch->acquire();
} }
@ -245,6 +246,7 @@ class MyThread: public Thread {
Assembler::Architecture* arch; Assembler::Architecture* arch;
Context* transition; Context* transition;
TraceContext* traceContext; TraceContext* traceContext;
uintptr_t stackLimit;
}; };
void void
@ -1118,6 +1120,7 @@ class Context {
objectPoolCount(0), objectPoolCount(0),
traceLogCount(0), traceLogCount(0),
dirtyRoots(false), dirtyRoots(false),
leaf(true),
eventLog(t->m->system, t->m->heap, 1024), eventLog(t->m->system, t->m->heap, 1024),
protector(this) protector(this)
{ } { }
@ -1139,6 +1142,7 @@ class Context {
objectPoolCount(0), objectPoolCount(0),
traceLogCount(0), traceLogCount(0),
dirtyRoots(false), dirtyRoots(false),
leaf(true),
eventLog(t->m->system, t->m->heap, 0), eventLog(t->m->system, t->m->heap, 0),
protector(this) protector(this)
{ } { }
@ -1164,6 +1168,7 @@ class Context {
unsigned objectPoolCount; unsigned objectPoolCount;
unsigned traceLogCount; unsigned traceLogCount;
bool dirtyRoots; bool dirtyRoots;
bool leaf;
Vector eventLog; Vector eventLog;
MyProtector protector; MyProtector protector;
}; };
@ -2099,6 +2104,9 @@ bootNativeThunk(MyThread* t);
uintptr_t uintptr_t
aioobThunk(MyThread* t); aioobThunk(MyThread* t);
uintptr_t
stackOverflowThunk(MyThread* t);
uintptr_t uintptr_t
virtualThunk(MyThread* t, unsigned index); virtualThunk(MyThread* t, unsigned index);
@ -2611,6 +2619,14 @@ throwArrayIndexOutOfBounds(MyThread* t)
unwind(t); unwind(t);
} }
void NO_RETURN
throwStackOverflow(MyThread* t)
{
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
unwind(t);
}
void NO_RETURN void NO_RETURN
throw_(MyThread* t, object o) throw_(MyThread* t, object o)
{ {
@ -4121,6 +4137,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
} break; } break;
case invokeinterface: { case invokeinterface: {
context->leaf = false;
uint16_t index = codeReadInt16(t, code, ip); uint16_t index = codeReadInt16(t, code, ip);
ip += 2; ip += 2;
@ -4162,6 +4180,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
} break; } break;
case invokespecial: { case invokespecial: {
context->leaf = false;
uint16_t index = codeReadInt16(t, code, ip); uint16_t index = codeReadInt16(t, code, ip);
object target = resolveMethod(t, context->method, index - 1); object target = resolveMethod(t, context->method, index - 1);
if (UNLIKELY(t->exception)) return; if (UNLIKELY(t->exception)) return;
@ -4179,6 +4199,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
} break; } break;
case invokestatic: { case invokestatic: {
context->leaf = false;
uint16_t index = codeReadInt16(t, code, ip); uint16_t index = codeReadInt16(t, code, ip);
object target = resolveMethod(t, context->method, index - 1); object target = resolveMethod(t, context->method, index - 1);
@ -4193,6 +4215,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
} break; } break;
case invokevirtual: { case invokevirtual: {
context->leaf = false;
uint16_t index = codeReadInt16(t, code, ip); uint16_t index = codeReadInt16(t, code, ip);
object target = resolveMethod(t, context->method, index - 1); object target = resolveMethod(t, context->method, index - 1);
@ -5745,7 +5769,9 @@ finish(MyThread* t, Allocator* allocator, Context* context)
// parallelism (the downside being that it may end up being a waste // parallelism (the downside being that it may end up being a waste
// of cycles if another thread compiles the same method in parallel, // of cycles if another thread compiles the same method in parallel,
// which might be mitigated by fine-grained, per-method locking): // which might be mitigated by fine-grained, per-method locking):
unsigned codeSize = c->compile(); unsigned codeSize = c->compile
(context->leaf ? 0 : stackOverflowThunk(t),
difference(&(t->stackLimit), t));
uintptr_t* code = static_cast<uintptr_t*> uintptr_t* code = static_cast<uintptr_t*>
(allocator->allocate(pad(codeSize) + pad(c->poolSize()) + BytesPerWord)); (allocator->allocate(pad(codeSize) + pad(c->poolSize()) + BytesPerWord));
@ -7092,6 +7118,15 @@ invoke(Thread* thread, object method, ArgumentList* arguments)
{ {
MyThread* t = static_cast<MyThread*>(thread); MyThread* t = static_cast<MyThread*>(thread);
uintptr_t stackLimit = t->stackLimit;
uintptr_t stackPosition = reinterpret_cast<uintptr_t>(&t);
if (stackLimit == 0) {
t->stackLimit = stackPosition - StackSizeInBytes;
} else if (stackPosition < stackLimit) {
t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
return 0;
}
unsigned returnCode = methodReturnCode(t, method); unsigned returnCode = methodReturnCode(t, method);
unsigned returnType = fieldType(t, returnCode); unsigned returnType = fieldType(t, returnCode);
@ -7111,6 +7146,8 @@ invoke(Thread* thread, object method, ArgumentList* arguments)
returnType); returnType);
} }
t->stackLimit = stackLimit;
if (t->exception) { if (t->exception) {
if (UNLIKELY(t->flags & Thread::UseBackupHeapFlag)) { if (UNLIKELY(t->flags & Thread::UseBackupHeapFlag)) {
collect(t, Heap::MinorCollection); collect(t, Heap::MinorCollection);
@ -7243,6 +7280,7 @@ class MyProcessor: public Processor {
Thunk defaultVirtual; Thunk defaultVirtual;
Thunk native; Thunk native;
Thunk aioob; Thunk aioob;
Thunk stackOverflow;
Thunk table; Thunk table;
}; };
@ -7801,7 +7839,7 @@ isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip)
bool bool
isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip) isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
{ {
const unsigned NamedThunkCount = 4; const unsigned NamedThunkCount = 5;
MyProcessor::Thunk table[NamedThunkCount + ThunkCount]; MyProcessor::Thunk table[NamedThunkCount + ThunkCount];
@ -7809,6 +7847,7 @@ isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
table[1] = thunks->defaultVirtual; table[1] = thunks->defaultVirtual;
table[2] = thunks->native; table[2] = thunks->native;
table[3] = thunks->aioob; table[3] = thunks->aioob;
table[4] = thunks->stackOverflow;
for (unsigned i = 0; i < ThunkCount; ++i) { for (unsigned i = 0; i < ThunkCount; ++i) {
new (table + NamedThunkCount + i) MyProcessor::Thunk new (table + NamedThunkCount + i) MyProcessor::Thunk
@ -8137,6 +8176,8 @@ fixupThunks(MyThread* t, BootImage* image, uint8_t* code)
= thunkToThunk(image->thunks.defaultVirtual, code); = thunkToThunk(image->thunks.defaultVirtual, code);
p->bootThunks.native = thunkToThunk(image->thunks.native, code); p->bootThunks.native = thunkToThunk(image->thunks.native, code);
p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code); p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code);
p->bootThunks.stackOverflow
= thunkToThunk(image->thunks.stackOverflow, code);
p->bootThunks.table = thunkToThunk(image->thunks.table, code); p->bootThunks.table = thunkToThunk(image->thunks.table, code);
updateCall(t, LongCall, code + image->compileMethodCall, updateCall(t, LongCall, code + image->compileMethodCall,
@ -8151,6 +8192,9 @@ fixupThunks(MyThread* t, BootImage* image, uint8_t* code)
updateCall(t, LongCall, code + image->throwArrayIndexOutOfBoundsCall, updateCall(t, LongCall, code + image->throwArrayIndexOutOfBoundsCall,
voidPointer(throwArrayIndexOutOfBounds)); voidPointer(throwArrayIndexOutOfBounds));
updateCall(t, LongCall, code + image->throwStackOverflowCall,
voidPointer(throwStackOverflow));
#define THUNK(s) \ #define THUNK(s) \
updateCall(t, LongJump, code + image->s##Call, voidPointer(s)); updateCall(t, LongJump, code + image->s##Call, voidPointer(s));
@ -8391,6 +8435,23 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p)
p->thunks.aioob.length = a->endBlock(false)->resolve(0, 0); p->thunks.aioob.length = a->endBlock(false)->resolve(0, 0);
} }
ThunkContext stackOverflowContext(t, &zone);
{ Assembler* a = stackOverflowContext.context.assembler;
a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t));
p->thunks.stackOverflow.frameSavedOffset = a->length();
Assembler::Register thread(t->arch->thread());
a->pushFrame(1, BytesPerWord, RegisterOperand, &thread);
Assembler::Constant proc(&(stackOverflowContext.promise));
a->apply(LongCall, BytesPerWord, ConstantOperand, &proc);
p->thunks.stackOverflow.length = a->endBlock(false)->resolve(0, 0);
}
ThunkContext tableContext(t, &zone); ThunkContext tableContext(t, &zone);
{ Assembler* a = tableContext.context.assembler; { Assembler* a = tableContext.context.assembler;
@ -8463,6 +8524,21 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p)
} }
} }
p->thunks.stackOverflow.start = finish
(t, allocator, stackOverflowContext.context.assembler, "stackOverflow",
p->thunks.stackOverflow.length);
{ void* call;
stackOverflowContext.promise.listener->resolve
(reinterpret_cast<intptr_t>(voidPointer(throwStackOverflow)),
&call);
if (image) {
image->throwStackOverflowCall
= static_cast<uint8_t*>(call) - imageBase;
}
}
p->thunks.table.start = static_cast<uint8_t*> p->thunks.table.start = static_cast<uint8_t*>
(allocator->allocate(p->thunks.table.length * ThunkCount)); (allocator->allocate(p->thunks.table.length * ThunkCount));
@ -8472,6 +8548,8 @@ compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p)
= thunkToThunk(p->thunks.defaultVirtual, imageBase); = thunkToThunk(p->thunks.defaultVirtual, imageBase);
image->thunks.native = thunkToThunk(p->thunks.native, imageBase); image->thunks.native = thunkToThunk(p->thunks.native, imageBase);
image->thunks.aioob = thunkToThunk(p->thunks.aioob, imageBase); image->thunks.aioob = thunkToThunk(p->thunks.aioob, imageBase);
image->thunks.stackOverflow
= thunkToThunk(p->thunks.stackOverflow, imageBase);
image->thunks.table = thunkToThunk(p->thunks.table, imageBase); image->thunks.table = thunkToThunk(p->thunks.table, imageBase);
} }
@ -8539,6 +8617,12 @@ aioobThunk(MyThread* t)
return reinterpret_cast<uintptr_t>(processor(t)->thunks.aioob.start); return reinterpret_cast<uintptr_t>(processor(t)->thunks.aioob.start);
} }
uintptr_t
stackOverflowThunk(MyThread* t)
{
return reinterpret_cast<uintptr_t>(processor(t)->thunks.stackOverflow.start);
}
bool bool
unresolved(MyThread* t, uintptr_t methodAddress) unresolved(MyThread* t, uintptr_t methodAddress)
{ {

@ -4919,8 +4919,7 @@ class BoundsCheckEvent: public Event {
lengthOffset, NoRegister, 1); lengthOffset, NoRegister, 1);
length.acquired = true; length.acquired = true;
CodePromise* nextPromise = codePromise CodePromise* nextPromise = codePromise(c, static_cast<Promise*>(0));
(c, static_cast<Promise*>(0));
freezeSource(c, BytesPerWord, index); freezeSource(c, BytesPerWord, index);
@ -5649,7 +5648,7 @@ block(Context* c, Event* head)
} }
unsigned unsigned
compile(Context* c) compile(Context* c, uintptr_t stackOverflowHandler, unsigned stackLimitOffset)
{ {
if (c->logicalCode[c->logicalIp]->lastEvent == 0) { if (c->logicalCode[c->logicalIp]->lastEvent == 0) {
appendDummy(c); appendDummy(c);
@ -5660,6 +5659,15 @@ compile(Context* c)
Block* firstBlock = block(c, c->firstEvent); Block* firstBlock = block(c, c->firstEvent);
Block* block = firstBlock; Block* block = firstBlock;
if (stackOverflowHandler) {
Assembler::Register stack(c->arch->stack());
Assembler::Memory stackLimit(c->arch->thread(), stackLimitOffset);
Assembler::Constant handler(resolved(c, stackOverflowHandler));
a->apply(JumpIfGreaterOrEqual, BytesPerWord, RegisterOperand, &stack,
BytesPerWord, MemoryOperand, &stackLimit,
BytesPerWord, ConstantOperand, &handler);
}
a->allocateFrame(c->alignedFrameSize); a->allocateFrame(c->alignedFrameSize);
for (Event* e = c->firstEvent; e; e = e->next) { for (Event* e = c->firstEvent; e; e = e->next) {
@ -6406,8 +6414,8 @@ class MyCompiler: public Compiler {
virtual void checkBounds(Operand* object, unsigned lengthOffset, virtual void checkBounds(Operand* object, unsigned lengthOffset,
Operand* index, intptr_t handler) Operand* index, intptr_t handler)
{ {
appendBoundsCheck(&c, static_cast<Value*>(object), appendBoundsCheck(&c, static_cast<Value*>(object), lengthOffset,
lengthOffset, static_cast<Value*>(index), handler); static_cast<Value*>(index), handler);
} }
virtual void store(unsigned srcSize, Operand* src, unsigned dstSize, virtual void store(unsigned srcSize, Operand* src, unsigned dstSize,
@ -6827,8 +6835,11 @@ class MyCompiler: public Compiler {
appendBarrier(&c, StoreLoadBarrier); appendBarrier(&c, StoreLoadBarrier);
} }
virtual unsigned compile() { virtual unsigned compile(uintptr_t stackOverflowHandler,
return c.machineCodeSize = local::compile(&c); unsigned stackLimitOffset)
{
return c.machineCodeSize = local::compile
(&c, stackOverflowHandler, stackLimitOffset);
} }
virtual unsigned poolSize() { virtual unsigned poolSize() {

@ -188,7 +188,8 @@ class Compiler {
virtual void storeStoreBarrier() = 0; virtual void storeStoreBarrier() = 0;
virtual void storeLoadBarrier() = 0; virtual void storeLoadBarrier() = 0;
virtual unsigned compile() = 0; virtual unsigned compile(uintptr_t stackOverflowHandler,
unsigned stackLimitOffset) = 0;
virtual unsigned poolSize() = 0; virtual unsigned poolSize() = 0;
virtual void writeTo(uint8_t* dst) = 0; virtual void writeTo(uint8_t* dst) = 0;

@ -30,9 +30,6 @@ class ClassInitList;
class Thread: public vm::Thread { class Thread: public vm::Thread {
public: public:
static const unsigned StackSizeInBytes = 64 * 1024;
static const unsigned StackSizeInWords = StackSizeInBytes / BytesPerWord;
Thread(Machine* m, object javaThread, vm::Thread* parent): Thread(Machine* m, object javaThread, vm::Thread* parent):
vm::Thread(m, javaThread, parent), vm::Thread(m, javaThread, parent),
ip(0), ip(0),
@ -78,7 +75,7 @@ pushObject(Thread* t, object o)
fprintf(stderr, "push object %p at %d\n", o, t->sp); fprintf(stderr, "push object %p at %d\n", o, t->sp);
} }
assert(t, t->sp + 1 < Thread::StackSizeInWords / 2); assert(t, t->sp + 1 < StackSizeInWords / 2);
t->stack[(t->sp * 2) ] = ObjectTag; t->stack[(t->sp * 2) ] = ObjectTag;
t->stack[(t->sp * 2) + 1] = reinterpret_cast<uintptr_t>(o); t->stack[(t->sp * 2) + 1] = reinterpret_cast<uintptr_t>(o);
++ t->sp; ++ t->sp;
@ -91,7 +88,7 @@ pushInt(Thread* t, uint32_t v)
fprintf(stderr, "push int %d at %d\n", v, t->sp); fprintf(stderr, "push int %d at %d\n", v, t->sp);
} }
assert(t, t->sp + 1 < Thread::StackSizeInWords / 2); assert(t, t->sp + 1 < StackSizeInWords / 2);
t->stack[(t->sp * 2) ] = IntTag; t->stack[(t->sp * 2) ] = IntTag;
t->stack[(t->sp * 2) + 1] = v; t->stack[(t->sp * 2) + 1] = v;
++ t->sp; ++ t->sp;
@ -184,7 +181,7 @@ peekObject(Thread* t, unsigned index)
index); index);
} }
assert(t, index < Thread::StackSizeInWords / 2); assert(t, index < StackSizeInWords / 2);
assert(t, t->stack[index * 2] == ObjectTag); assert(t, t->stack[index * 2] == ObjectTag);
return *reinterpret_cast<object*>(t->stack + (index * 2) + 1); return *reinterpret_cast<object*>(t->stack + (index * 2) + 1);
} }
@ -198,7 +195,7 @@ peekInt(Thread* t, unsigned index)
index); index);
} }
assert(t, index < Thread::StackSizeInWords / 2); assert(t, index < StackSizeInWords / 2);
assert(t, t->stack[index * 2] == IntTag); assert(t, t->stack[index * 2] == IntTag);
return t->stack[(index * 2) + 1]; return t->stack[(index * 2) + 1];
} }
@ -254,7 +251,7 @@ inline object*
pushReference(Thread* t, object o) pushReference(Thread* t, object o)
{ {
if (o) { if (o) {
expect(t, t->sp + 1 < Thread::StackSizeInWords / 2); expect(t, t->sp + 1 < StackSizeInWords / 2);
pushObject(t, o); pushObject(t, o);
return reinterpret_cast<object*>(t->stack + ((t->sp - 1) * 2) + 1); return reinterpret_cast<object*>(t->stack + ((t->sp - 1) * 2) + 1);
} else { } else {
@ -436,7 +433,7 @@ checkStack(Thread* t, object method)
+ codeMaxLocals(t, methodCode(t, method)) + codeMaxLocals(t, methodCode(t, method))
+ FrameFootprint + FrameFootprint
+ codeMaxStack(t, methodCode(t, method)) + codeMaxStack(t, methodCode(t, method))
> Thread::StackSizeInWords / 2)) > StackSizeInWords / 2))
{ {
t->exception = makeThrowable(t, Machine::StackOverflowErrorType); t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
} }
@ -3136,7 +3133,7 @@ class MyProcessor: public Processor {
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0)); assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1 if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
> Thread::StackSizeInWords / 2)) > StackSizeInWords / 2))
{ {
t->exception = makeThrowable(t, Machine::StackOverflowErrorType); t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
return 0; return 0;
@ -3161,7 +3158,7 @@ class MyProcessor: public Processor {
assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0)); assert(t, ((methodFlags(t, method) & ACC_STATIC) == 0) xor (this_ == 0));
if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1 if (UNLIKELY(t->sp + methodParameterFootprint(t, method) + 1
> Thread::StackSizeInWords / 2)) > StackSizeInWords / 2))
{ {
t->exception = makeThrowable(t, Machine::StackOverflowErrorType); t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
return 0; return 0;
@ -3185,7 +3182,7 @@ class MyProcessor: public Processor {
or t->state == Thread::ExclusiveState); or t->state == Thread::ExclusiveState);
if (UNLIKELY(t->sp + parameterFootprint(vmt, methodSpec, false) if (UNLIKELY(t->sp + parameterFootprint(vmt, methodSpec, false)
> Thread::StackSizeInWords / 2)) > StackSizeInWords / 2))
{ {
t->exception = makeThrowable(t, Machine::StackOverflowErrorType); t->exception = makeThrowable(t, Machine::StackOverflowErrorType);
return 0; return 0;

@ -56,6 +56,9 @@ const unsigned ThreadBackupHeapSizeInBytes = 2 * 1024;
const unsigned ThreadBackupHeapSizeInWords const unsigned ThreadBackupHeapSizeInWords
= ThreadBackupHeapSizeInBytes / BytesPerWord; = ThreadBackupHeapSizeInBytes / BytesPerWord;
const unsigned StackSizeInBytes = 64 * 1024;
const unsigned StackSizeInWords = StackSizeInBytes / BytesPerWord;
const unsigned ThreadHeapPoolSize = 64; const unsigned ThreadHeapPoolSize = 64;
const unsigned FixedFootprintThresholdInBytes const unsigned FixedFootprintThresholdInBytes

29
test/StackOverflow.java Normal file

@ -0,0 +1,29 @@
public class StackOverflow {
private static void test1() {
test1();
}
private static void test2() {
test3();
}
private static void test3() {
test2();
}
public static void main(String[] args) {
try {
test1();
throw new RuntimeException();
} catch (StackOverflowError e) {
e.printStackTrace();
}
try {
test2();
throw new RuntimeException();
} catch (StackOverflowError e) {
e.printStackTrace();
}
}
}