mirror of
https://github.com/corda/corda.git
synced 2025-01-03 19:54:13 +00:00
fix custom-classloader-related concurrency problems and other bugs
The main changes in this commit ensure that we don't hold the global class lock when doing class resolution using application-defined classloaders. Such classloaders may do their own locking (in fact, it's almost certain), making deadlock likely when mixed with VM-level locking in various orders. Other changes include a fix to avoid overflow when waiting for extremely long intervals and a GC root stack mapping bug.
This commit is contained in:
parent
f485016637
commit
d0d53e2e10
@ -15,7 +15,7 @@ import java.util.WeakHashMap;
|
||||
|
||||
public class Thread implements Runnable {
|
||||
private long peer;
|
||||
private boolean interrupted;
|
||||
private volatile boolean interrupted;
|
||||
private boolean daemon;
|
||||
private byte state;
|
||||
private byte priority;
|
||||
@ -141,25 +141,9 @@ public class Thread implements Runnable {
|
||||
|
||||
public static native Thread currentThread();
|
||||
|
||||
private static native void interrupt(long peer);
|
||||
public native void interrupt();
|
||||
|
||||
public synchronized void interrupt() {
|
||||
if (peer != 0) {
|
||||
interrupt(peer);
|
||||
} else {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean interrupted() {
|
||||
Thread t = currentThread();
|
||||
|
||||
synchronized (t) {
|
||||
boolean v = t.interrupted;
|
||||
t.interrupted = false;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
public native boolean interrupted();
|
||||
|
||||
public static boolean isInterrupted() {
|
||||
return currentThread().interrupted;
|
||||
|
4
makefile
4
makefile
@ -340,10 +340,6 @@ vm-sources = \
|
||||
vm-asm-sources = $(src)/$(asm).S
|
||||
|
||||
ifeq ($(process),compile)
|
||||
vm-depends += \
|
||||
$(src)/compiler.h \
|
||||
$(src)/vector.h
|
||||
|
||||
vm-sources += \
|
||||
$(src)/compiler.cpp \
|
||||
$(src)/$(asm).cpp
|
||||
|
@ -84,7 +84,12 @@ Avian_avian_SystemClassLoader_resourceExists
|
||||
if (LIKELY(name)) {
|
||||
RUNTIME_ARRAY(char, n, stringLength(t, name) + 1);
|
||||
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
|
||||
return getFinder(t, loader)->exists(RUNTIME_ARRAY_BODY(n));
|
||||
|
||||
bool r = getFinder(t, loader)->exists(RUNTIME_ARRAY_BODY(n));
|
||||
|
||||
fprintf(stderr, "resource %s exists? %d\n", n, r);
|
||||
|
||||
return r;
|
||||
} else {
|
||||
t->exception = t->m->classpath->makeThrowable
|
||||
(t, Machine::NullPointerExceptionType);
|
||||
@ -144,7 +149,11 @@ Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength
|
||||
RUNTIME_ARRAY(char, p, stringLength(t, path) + 1);
|
||||
stringChars(t, path, RUNTIME_ARRAY_BODY(p));
|
||||
|
||||
System::Region* r = t->m->appFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
if (r == 0) {
|
||||
r = t->m->appFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
}
|
||||
|
||||
if (r) {
|
||||
jint rSize = r->length();
|
||||
r->dispose();
|
||||
@ -164,8 +173,12 @@ Avian_avian_resource_Handler_00024ResourceInputStream_open
|
||||
RUNTIME_ARRAY(char, p, stringLength(t, path) + 1);
|
||||
stringChars(t, path, RUNTIME_ARRAY_BODY(p));
|
||||
|
||||
return reinterpret_cast<int64_t>
|
||||
(t->m->appFinder->find(RUNTIME_ARRAY_BODY(p)));
|
||||
System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
if (r == 0) {
|
||||
r = t->m->appFinder->find(RUNTIME_ARRAY_BODY(p));
|
||||
}
|
||||
|
||||
return reinterpret_cast<int64_t>(r);
|
||||
} else {
|
||||
t->exception = t->m->classpath->makeThrowable
|
||||
(t, Machine::NullPointerExceptionType);
|
||||
|
@ -50,7 +50,7 @@ class MyClasspath : public Classpath {
|
||||
|
||||
return vm::makeThread
|
||||
(t, 0, 0, 0, NewState, NormalPriority, 0, 0, 0,
|
||||
root(t, Machine::BootLoader), 0, 0, group);
|
||||
root(t, Machine::BootLoader), 0, 0, group, 0);
|
||||
}
|
||||
|
||||
virtual void
|
||||
@ -70,6 +70,7 @@ class MyClasspath : public Classpath {
|
||||
(Thread* t, Machine::Type type, object message, object trace, object cause)
|
||||
{
|
||||
PROTECT(t, message);
|
||||
PROTECT(t, trace);
|
||||
PROTECT(t, cause);
|
||||
|
||||
if (trace == 0) {
|
||||
|
@ -186,7 +186,7 @@ class MyClasspath : public Classpath {
|
||||
return vm::makeThread
|
||||
(t, 0, NormalPriority, 0, 0, false, false, false, 0, group,
|
||||
root(t, Machine::BootLoader), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, 0,
|
||||
0, false);
|
||||
0, 0, false);
|
||||
}
|
||||
|
||||
virtual void
|
||||
@ -210,6 +210,7 @@ class MyClasspath : public Classpath {
|
||||
(Thread* t, Machine::Type type, object message, object trace, object cause)
|
||||
{
|
||||
PROTECT(t, message);
|
||||
PROTECT(t, trace);
|
||||
PROTECT(t, cause);
|
||||
|
||||
if (trace == 0) {
|
||||
@ -534,6 +535,23 @@ setProperty(Thread* t, object method, object properties,
|
||||
t->m->processor->invoke(t, method, properties, n, v);
|
||||
}
|
||||
|
||||
object
|
||||
interruptLock(Thread* t, object thread)
|
||||
{
|
||||
if (threadInterruptLock(t, thread) == 0) {
|
||||
PROTECT(t, thread);
|
||||
ACQUIRE(t, t->m->referenceLock);
|
||||
|
||||
if (threadInterruptLock(t, thread) == 0) {
|
||||
object head = makeMonitorNode(t, 0, 0);
|
||||
object lock = makeMonitor(t, 0, 0, 0, head, head, 0);
|
||||
set(t, thread, ThreadInterruptLock, lock);
|
||||
}
|
||||
}
|
||||
|
||||
return threadInterruptLock(t, thread);
|
||||
}
|
||||
|
||||
} // namespace local
|
||||
|
||||
} // namespace
|
||||
@ -1255,10 +1273,14 @@ JVM_Interrupt(Thread* t, jobject thread)
|
||||
{
|
||||
ENTER(t, Thread::ActiveState);
|
||||
|
||||
monitorAcquire(t, local::interruptLock(t, *thread));
|
||||
Thread* p = reinterpret_cast<Thread*>(threadPeer(t, *thread));
|
||||
if (p) {
|
||||
interrupt(t, p);
|
||||
} else {
|
||||
threadInterrupted(t, *thread) = true;
|
||||
}
|
||||
monitorRelease(t, local::interruptLock(t, *thread));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
@ -1266,12 +1288,12 @@ JVM_IsInterrupted(Thread* t, jobject thread, jboolean clear)
|
||||
{
|
||||
ENTER(t, Thread::ActiveState);
|
||||
|
||||
acquire(t, *thread);
|
||||
monitorAcquire(t, local::interruptLock(t, *thread));
|
||||
bool v = threadInterrupted(t, *thread);
|
||||
if (clear) {
|
||||
threadInterrupted(t, *thread) = false;
|
||||
}
|
||||
release(t, *thread);
|
||||
monitorRelease(t, local::interruptLock(t, *thread));
|
||||
|
||||
return v;
|
||||
}
|
||||
@ -2414,7 +2436,10 @@ extern "C" JNIEXPORT struct hostent* JNICALL
|
||||
JVM_GetHostByName(char*) { abort(); }
|
||||
|
||||
extern "C" JNIEXPORT int JNICALL
|
||||
JVM_GetHostName(char*, int) { abort(); }
|
||||
JVM_GetHostName(char* name, int length)
|
||||
{
|
||||
return gethostname(name, length);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void* JNICALL
|
||||
JVM_RawMonitorCreate(void)
|
||||
|
284
src/compile.cpp
284
src/compile.cpp
@ -327,6 +327,12 @@ compiledSize(intptr_t address)
|
||||
return reinterpret_cast<uintptr_t*>(address)[-1];
|
||||
}
|
||||
|
||||
intptr_t
|
||||
methodCompiled(Thread* t, object method)
|
||||
{
|
||||
return codeCompiled(t, methodCode(t, method));
|
||||
}
|
||||
|
||||
intptr_t
|
||||
compareIpToMethodBounds(Thread* t, intptr_t ip, object method)
|
||||
{
|
||||
@ -2619,7 +2625,7 @@ throw_(MyThread* t, object o)
|
||||
(t, Machine::NullPointerExceptionType);
|
||||
}
|
||||
|
||||
// printTrace(t, t->exception);
|
||||
printTrace(t, t->exception);
|
||||
|
||||
unwind(t);
|
||||
}
|
||||
@ -2659,6 +2665,8 @@ makeNew64(Thread* t, object class_)
|
||||
void
|
||||
gcIfNecessary(MyThread* t)
|
||||
{
|
||||
stress(t);
|
||||
|
||||
if (UNLIKELY(t->flags & Thread::UseBackupHeapFlag)) {
|
||||
collect(t, Heap::MinorCollection);
|
||||
}
|
||||
@ -3219,6 +3227,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
||||
|
||||
if (inTryBlock(t, code, ip - 1)) {
|
||||
c->saveLocals();
|
||||
frame->trace(0, 0);
|
||||
}
|
||||
|
||||
if (CheckArrayBounds) {
|
||||
@ -3311,6 +3320,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
||||
|
||||
if (inTryBlock(t, code, ip - 1)) {
|
||||
c->saveLocals();
|
||||
frame->trace(0, 0);
|
||||
}
|
||||
|
||||
if (CheckArrayBounds) {
|
||||
@ -3741,6 +3751,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
||||
|
||||
if (inTryBlock(t, code, ip - 3)) {
|
||||
c->saveLocals();
|
||||
frame->trace(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4709,6 +4720,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip,
|
||||
|
||||
if (inTryBlock(t, code, ip - 3)) {
|
||||
c->saveLocals();
|
||||
frame->trace(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5006,7 +5018,7 @@ logCompile(MyThread* t, const void* code, unsigned size, const char* class_,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
object
|
||||
translateExceptionHandlerTable(MyThread* t, Compiler* c, object method,
|
||||
intptr_t start)
|
||||
{
|
||||
@ -5043,7 +5055,7 @@ translateExceptionHandlerTable(MyThread* t, Compiler* c, object method,
|
||||
if (exceptionHandlerCatchType(oldHandler)) {
|
||||
type = resolveClassInPool
|
||||
(t, method, exceptionHandlerCatchType(oldHandler) - 1);
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
} else {
|
||||
type = 0;
|
||||
}
|
||||
@ -5051,11 +5063,13 @@ translateExceptionHandlerTable(MyThread* t, Compiler* c, object method,
|
||||
set(t, newTable, ArrayBody + ((i + 1) * BytesPerWord), type);
|
||||
}
|
||||
|
||||
set(t, methodCode(t, method), CodeExceptionHandlerTable, newTable);
|
||||
return newTable;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
object
|
||||
translateLineNumberTable(MyThread* t, Compiler* c, object code, intptr_t start)
|
||||
{
|
||||
object oldTable = codeLineNumberTable(t, code);
|
||||
@ -5075,7 +5089,9 @@ translateLineNumberTable(MyThread* t, Compiler* c, object code, intptr_t start)
|
||||
lineNumberLine(newLine) = lineNumberLine(oldLine);
|
||||
}
|
||||
|
||||
set(t, code, CodeLineNumberTable, newTable);
|
||||
return newTable;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5527,23 +5543,23 @@ compareSubroutineTracePointers(const void* va, const void* vb)
|
||||
|
||||
object
|
||||
makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start,
|
||||
TraceElement** elements, unsigned pathFootprint,
|
||||
unsigned mapCount)
|
||||
TraceElement** elements, unsigned elementCount,
|
||||
unsigned pathFootprint, unsigned mapCount)
|
||||
{
|
||||
unsigned mapSize = frameMapSizeInBits(t, context->method);
|
||||
unsigned indexOffset = sizeof(FrameMapTableHeader);
|
||||
unsigned mapsOffset = indexOffset
|
||||
+ (context->traceLogCount * sizeof(FrameMapTableIndexElement));
|
||||
+ (elementCount * sizeof(FrameMapTableIndexElement));
|
||||
unsigned pathsOffset = mapsOffset + (ceiling(mapCount * mapSize, 32) * 4);
|
||||
|
||||
object table = makeByteArray(t, pathsOffset + pathFootprint);
|
||||
|
||||
int8_t* body = &byteArrayBody(t, table, 0);
|
||||
new (body) FrameMapTableHeader(context->traceLogCount);
|
||||
new (body) FrameMapTableHeader(elementCount);
|
||||
|
||||
unsigned nextTableIndex = pathsOffset;
|
||||
unsigned nextMapIndex = 0;
|
||||
for (unsigned i = 0; i < context->traceLogCount; ++i) {
|
||||
for (unsigned i = 0; i < elementCount; ++i) {
|
||||
TraceElement* p = elements[i];
|
||||
unsigned mapBase = nextMapIndex;
|
||||
|
||||
@ -5645,27 +5661,26 @@ makeGeneralFrameMapTable(MyThread* t, Context* context, uint8_t* start,
|
||||
|
||||
object
|
||||
makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start,
|
||||
TraceElement** elements)
|
||||
TraceElement** elements, unsigned elementCount)
|
||||
{
|
||||
unsigned mapSize = frameMapSizeInBits(t, context->method);
|
||||
object table = makeIntArray
|
||||
(t, context->traceLogCount
|
||||
+ ceiling(context->traceLogCount * mapSize, 32));
|
||||
(t, elementCount + ceiling(elementCount * mapSize, 32));
|
||||
|
||||
assert(t, intArrayLength(t, table) == context->traceLogCount
|
||||
assert(t, intArrayLength(t, table) == elementCount
|
||||
+ simpleFrameMapTableSize(t, context->method, table));
|
||||
|
||||
for (unsigned i = 0; i < context->traceLogCount; ++i) {
|
||||
for (unsigned i = 0; i < elementCount; ++i) {
|
||||
TraceElement* p = elements[i];
|
||||
|
||||
intArrayBody(t, table, i) = static_cast<intptr_t>(p->address->value())
|
||||
- reinterpret_cast<intptr_t>(start);
|
||||
|
||||
assert(t, context->traceLogCount + ceiling((i + 1) * mapSize, 32)
|
||||
assert(t, elementCount + ceiling((i + 1) * mapSize, 32)
|
||||
<= intArrayLength(t, table));
|
||||
|
||||
if (mapSize) {
|
||||
copyFrameMap(&intArrayBody(t, table, context->traceLogCount), p->map,
|
||||
copyFrameMap(&intArrayBody(t, table, elementCount), p->map,
|
||||
mapSize, i * mapSize, p, 0);
|
||||
}
|
||||
}
|
||||
@ -5673,7 +5688,7 @@ makeSimpleFrameMapTable(MyThread* t, Context* context, uint8_t* start,
|
||||
return table;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
void
|
||||
finish(MyThread* t, Allocator* allocator, Context* context)
|
||||
{
|
||||
Compiler* c = context->compiler;
|
||||
@ -5703,7 +5718,13 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
||||
trap();
|
||||
}
|
||||
|
||||
// todo: this is a CPU-intensive operation, so consider doing it
|
||||
// earlier before we've acquired the global class lock to improve
|
||||
// parallelism (the downside being that it may end up being a waste
|
||||
// of cycles if another thread compiles the same method in parallel,
|
||||
// which might be mitigated by fine-grained, per-method locking):
|
||||
unsigned codeSize = c->compile();
|
||||
|
||||
uintptr_t* code = static_cast<uintptr_t*>
|
||||
(allocator->allocate(pad(codeSize) + pad(c->poolSize()) + BytesPerWord));
|
||||
code[0] = codeSize;
|
||||
@ -5744,21 +5765,20 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
||||
}
|
||||
}
|
||||
|
||||
translateExceptionHandlerTable
|
||||
object newExceptionHandlerTable = translateExceptionHandlerTable
|
||||
(t, c, context->method, reinterpret_cast<intptr_t>(start));
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
PROTECT(t, newExceptionHandlerTable);
|
||||
|
||||
translateLineNumberTable(t, c, methodCode(t, context->method),
|
||||
reinterpret_cast<intptr_t>(start));
|
||||
object newLineNumberTable = translateLineNumberTable
|
||||
(t, c, methodCode(t, context->method), reinterpret_cast<intptr_t>(start));
|
||||
|
||||
{ object code = methodCode(t, context->method);
|
||||
|
||||
code = makeCode(t, 0,
|
||||
codeExceptionHandlerTable(t, code),
|
||||
codeLineNumberTable(t, code),
|
||||
codeMaxStack(t, code),
|
||||
codeMaxLocals(t, code),
|
||||
0);
|
||||
code = makeCode
|
||||
(t, 0, newExceptionHandlerTable, newLineNumberTable,
|
||||
reinterpret_cast<uintptr_t>(start), codeMaxStack(t, code),
|
||||
codeMaxLocals(t, code), 0);
|
||||
|
||||
set(t, context->method, MethodCode, code);
|
||||
}
|
||||
@ -5771,44 +5791,46 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
||||
for (TraceElement* p = context->traceLog; p; p = p->next) {
|
||||
assert(t, index < context->traceLogCount);
|
||||
|
||||
SubroutineTrace* trace = p->subroutineTrace;
|
||||
unsigned myMapCount = 1;
|
||||
if (trace) {
|
||||
for (Subroutine* s = trace->path->call->subroutine;
|
||||
s; s = s->stackNext)
|
||||
{
|
||||
unsigned callCount = s->callCount;
|
||||
myMapCount *= callCount;
|
||||
if (not s->visited) {
|
||||
s->visited = true;
|
||||
pathFootprint += sizeof(FrameMapTablePath)
|
||||
+ (sizeof(int32_t) * callCount);
|
||||
if (p->address) {
|
||||
SubroutineTrace* trace = p->subroutineTrace;
|
||||
unsigned myMapCount = 1;
|
||||
if (trace) {
|
||||
for (Subroutine* s = trace->path->call->subroutine;
|
||||
s; s = s->stackNext)
|
||||
{
|
||||
unsigned callCount = s->callCount;
|
||||
myMapCount *= callCount;
|
||||
if (not s->visited) {
|
||||
s->visited = true;
|
||||
pathFootprint += sizeof(FrameMapTablePath)
|
||||
+ (sizeof(int32_t) * callCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapCount += myMapCount;
|
||||
mapCount += myMapCount;
|
||||
|
||||
RUNTIME_ARRAY_BODY(elements)[index++] = p;
|
||||
RUNTIME_ARRAY_BODY(elements)[index++] = p;
|
||||
|
||||
if (p->target) {
|
||||
insertCallNode
|
||||
(t, makeCallNode
|
||||
(t, p->address->value(), p->target, p->flags, 0));
|
||||
if (p->target) {
|
||||
insertCallNode
|
||||
(t, makeCallNode
|
||||
(t, p->address->value(), p->target, p->flags, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qsort(RUNTIME_ARRAY_BODY(elements), context->traceLogCount,
|
||||
qsort(RUNTIME_ARRAY_BODY(elements), index,
|
||||
sizeof(TraceElement*), compareTraceElementPointers);
|
||||
|
||||
object map;
|
||||
if (pathFootprint) {
|
||||
map = makeGeneralFrameMapTable
|
||||
(t, context, start, RUNTIME_ARRAY_BODY(elements), pathFootprint,
|
||||
(t, context, start, RUNTIME_ARRAY_BODY(elements), index, pathFootprint,
|
||||
mapCount);
|
||||
} else {
|
||||
map = makeSimpleFrameMapTable
|
||||
(t, context, start, RUNTIME_ARRAY_BODY(elements));
|
||||
(t, context, start, RUNTIME_ARRAY_BODY(elements), index);
|
||||
}
|
||||
|
||||
set(t, methodCode(t, context->method), CodePool, map);
|
||||
@ -5838,12 +5860,10 @@ finish(MyThread* t, Allocator* allocator, Context* context)
|
||||
}
|
||||
|
||||
syncInstructionCache(start, codeSize);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
compile(MyThread* t, Allocator* allocator, Context* context)
|
||||
void
|
||||
compile(MyThread* t, Context* context)
|
||||
{
|
||||
Compiler* c = context->compiler;
|
||||
|
||||
@ -5908,7 +5928,7 @@ compile(MyThread* t, Allocator* allocator, Context* context)
|
||||
Compiler::State* state = c->saveState();
|
||||
|
||||
compile(t, &frame, 0);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
context->dirtyRoots = false;
|
||||
unsigned eventIndex = calculateFrameMaps(t, context, 0, 0);
|
||||
@ -5961,7 +5981,7 @@ compile(MyThread* t, Allocator* allocator, Context* context)
|
||||
}
|
||||
|
||||
compile(t, &frame2, exceptionHandlerIp(eh), start);
|
||||
if (UNLIKELY(t->exception)) return 0;
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
context->eventLog.append(PopContextEvent);
|
||||
|
||||
@ -5977,8 +5997,6 @@ compile(MyThread* t, Allocator* allocator, Context* context)
|
||||
context->dirtyRoots = false;
|
||||
calculateFrameMaps(t, context, 0, 0);
|
||||
}
|
||||
|
||||
return finish(t, allocator, context);
|
||||
}
|
||||
|
||||
void
|
||||
@ -7169,7 +7187,7 @@ class SegFaultHandler: public System::SignalHandler {
|
||||
t->exception = root(t, Machine::NullPointerException);
|
||||
}
|
||||
|
||||
// printTrace(t, t->exception);
|
||||
printTrace(t, t->exception);
|
||||
|
||||
object continuation;
|
||||
findUnwindTarget(t, ip, base, stack, &continuation);
|
||||
@ -7283,10 +7301,13 @@ class MyProcessor: public Processor {
|
||||
object class_,
|
||||
object code)
|
||||
{
|
||||
if (code) {
|
||||
codeCompiled(t, code) = local::defaultThunk(static_cast<MyThread*>(t));
|
||||
}
|
||||
|
||||
return vm::makeMethod
|
||||
(t, vmFlags, returnCode, parameterCount, parameterFootprint, flags,
|
||||
offset, 0, name, spec, addendum, class_, code,
|
||||
local::defaultThunk(static_cast<MyThread*>(t)));
|
||||
offset, 0, name, spec, addendum, class_, code);
|
||||
}
|
||||
|
||||
virtual object
|
||||
@ -8083,7 +8104,7 @@ fixupMethods(Thread* t, object map, BootImage* image, uint8_t* code)
|
||||
assert(t, (methodCompiled(t, method) - image->codeBase)
|
||||
<= image->codeSize);
|
||||
|
||||
methodCompiled(t, method)
|
||||
codeCompiled(t, methodCode(t, method))
|
||||
= (methodCompiled(t, method) - image->codeBase)
|
||||
+ reinterpret_cast<uintptr_t>(code);
|
||||
|
||||
@ -8597,65 +8618,86 @@ compile(MyThread* t, Allocator* allocator, BootContext* bootContext,
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
}
|
||||
|
||||
if (methodAddress(t, method) == defaultThunk(t)) {
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
|
||||
if (methodAddress(t, method) == defaultThunk(t)) {
|
||||
assert(t, (methodFlags(t, method) & ACC_NATIVE) == 0);
|
||||
|
||||
Context context(t, bootContext, method);
|
||||
uint8_t* compiled = compile(t, allocator, &context);
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
if (DebugMethodTree) {
|
||||
fprintf(stderr, "insert method at %p\n", compiled);
|
||||
}
|
||||
|
||||
// We can't set the MethodCompiled field on the original method
|
||||
// before it is placed into the method tree, since another
|
||||
// thread might call the method, from which stack unwinding
|
||||
// would fail (since there is not yet an entry in the method
|
||||
// tree). However, we can't insert the original method into the
|
||||
// tree before setting the MethodCompiled field on it since we
|
||||
// rely on that field to determine its position in the tree.
|
||||
// Therefore, we insert a clone in its place. Later, we'll
|
||||
// replace the clone with the original to save memory.
|
||||
|
||||
object clone = makeMethod
|
||||
(t, methodVmFlags(t, method),
|
||||
methodReturnCode(t, method),
|
||||
methodParameterCount(t, method),
|
||||
methodParameterFootprint(t, method),
|
||||
methodFlags(t, method),
|
||||
methodOffset(t, method),
|
||||
methodNativeID(t, method),
|
||||
methodName(t, method),
|
||||
methodSpec(t, method),
|
||||
methodAddendum(t, method),
|
||||
methodClass(t, method),
|
||||
methodCode(t, method),
|
||||
reinterpret_cast<intptr_t>(compiled));
|
||||
|
||||
setRoot
|
||||
(t, MethodTree, treeInsert
|
||||
(t, &(context.zone), root(t, MethodTree),
|
||||
reinterpret_cast<intptr_t>(compiled), clone,
|
||||
root(t, MethodTreeSentinal),
|
||||
compareIpToMethodBounds));
|
||||
|
||||
storeStoreMemoryBarrier();
|
||||
|
||||
methodCompiled(t, method) = reinterpret_cast<intptr_t>(compiled);
|
||||
|
||||
if (methodVirtual(t, method)) {
|
||||
classVtable(t, methodClass(t, method), methodOffset(t, method))
|
||||
= compiled;
|
||||
}
|
||||
|
||||
treeUpdate(t, root(t, MethodTree), reinterpret_cast<intptr_t>(compiled),
|
||||
method, root(t, MethodTreeSentinal), compareIpToMethodBounds);
|
||||
}
|
||||
if (methodAddress(t, method) != defaultThunk(t)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(t, (methodFlags(t, method) & ACC_NATIVE) == 0);
|
||||
|
||||
// We must avoid acquiring any locks until after the first pass of
|
||||
// compilation, since this pass may trigger classloading operations
|
||||
// involving application classloaders and thus the potential for
|
||||
// deadlock. To make this safe, we use a private clone of the
|
||||
// method so that we won't be confused if another thread updates the
|
||||
// original while we're working.
|
||||
|
||||
object clone = makeMethod
|
||||
(t, methodVmFlags(t, method),
|
||||
methodReturnCode(t, method),
|
||||
methodParameterCount(t, method),
|
||||
methodParameterFootprint(t, method),
|
||||
methodFlags(t, method),
|
||||
methodOffset(t, method),
|
||||
methodNativeID(t, method),
|
||||
methodName(t, method),
|
||||
methodSpec(t, method),
|
||||
methodAddendum(t, method),
|
||||
methodClass(t, method),
|
||||
methodCode(t, method));
|
||||
|
||||
loadMemoryBarrier();
|
||||
|
||||
if (methodAddress(t, method) != defaultThunk(t)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PROTECT(t, clone);
|
||||
|
||||
Context context(t, bootContext, clone);
|
||||
compile(t, &context);
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
|
||||
if (methodAddress(t, method) != defaultThunk(t)) {
|
||||
return;
|
||||
}
|
||||
|
||||
finish(t, allocator, &context);
|
||||
if (UNLIKELY(t->exception)) return;
|
||||
|
||||
if (DebugMethodTree) {
|
||||
fprintf(stderr, "insert method at %p\n",
|
||||
reinterpret_cast<void*>(methodCompiled(t, clone)));
|
||||
}
|
||||
|
||||
// We can't update the MethodCode field on the original method
|
||||
// before it is placed into the method tree, since another thread
|
||||
// might call the method, from which stack unwinding would fail
|
||||
// (since there is not yet an entry in the method tree). However,
|
||||
// we can't insert the original method into the tree before updating
|
||||
// the MethodCode field on it since we rely on that field to
|
||||
// determine its position in the tree. Therefore, we insert the
|
||||
// clone in its place. Later, we'll replace the clone with the
|
||||
// original to save memory.
|
||||
|
||||
setRoot
|
||||
(t, MethodTree, treeInsert
|
||||
(t, &(context.zone), root(t, MethodTree),
|
||||
methodCompiled(t, clone), clone, root(t, MethodTreeSentinal),
|
||||
compareIpToMethodBounds));
|
||||
|
||||
storeStoreMemoryBarrier();
|
||||
|
||||
set(t, method, MethodCode, methodCode(t, clone));
|
||||
|
||||
if (methodVirtual(t, method)) {
|
||||
classVtable(t, methodClass(t, method), methodOffset(t, method))
|
||||
= reinterpret_cast<void*>(methodCompiled(t, clone));
|
||||
}
|
||||
|
||||
treeUpdate(t, root(t, MethodTree), methodCompiled(t, clone),
|
||||
method, root(t, MethodTreeSentinal), compareIpToMethodBounds);
|
||||
}
|
||||
|
||||
object&
|
||||
|
@ -2007,11 +2007,11 @@ parseSize(const char* s)
|
||||
return 0;
|
||||
} else if (s[length - 1] == 'k') {
|
||||
memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1);
|
||||
RUNTIME_ARRAY_BODY(buffer)[length] = 0;
|
||||
RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0;
|
||||
return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024;
|
||||
} else if (s[length - 1] == 'm') {
|
||||
memcpy(RUNTIME_ARRAY_BODY(buffer), s, length - 1);
|
||||
RUNTIME_ARRAY_BODY(buffer)[length] = 0;
|
||||
RUNTIME_ARRAY_BODY(buffer)[length - 1] = 0;
|
||||
return atoi(RUNTIME_ARRAY_BODY(buffer)) * 1024 * 1024;
|
||||
} else {
|
||||
return atoi(s);
|
||||
|
140
src/machine.cpp
140
src/machine.cpp
@ -726,6 +726,8 @@ internByteArray(Thread* t, object array)
|
||||
{
|
||||
PROTECT(t, array);
|
||||
|
||||
ACQUIRE(t, t->m->referenceLock);
|
||||
|
||||
object n = hashMapFindNode
|
||||
(t, root(t, Machine::ByteArrayMap), array, byteArrayHash, byteArrayEqual);
|
||||
if (n) {
|
||||
@ -1199,7 +1201,7 @@ parseCode(Thread* t, Stream& s, object pool)
|
||||
unsigned maxLocals = s.read2();
|
||||
unsigned length = s.read4();
|
||||
|
||||
object code = makeCode(t, pool, 0, 0, maxStack, maxLocals, length);
|
||||
object code = makeCode(t, pool, 0, 0, 0, maxStack, maxLocals, length);
|
||||
s.read(&codeBody(t, code, 0), length);
|
||||
PROTECT(t, code);
|
||||
|
||||
@ -1297,7 +1299,6 @@ addInterfaceMethods(Thread* t, object class_, object virtualMap,
|
||||
methodSpec(t, method),
|
||||
0,
|
||||
class_,
|
||||
0,
|
||||
0);
|
||||
|
||||
hashMapInsert(t, virtualMap, method, method, methodHash);
|
||||
@ -1705,6 +1706,7 @@ makeArrayClass(Thread* t, object loader, unsigned dimensions, object spec,
|
||||
// todo: arrays should implement Cloneable and Serializable
|
||||
|
||||
if (classVmFlags(t, type(t, Machine::JobjectType)) & BootstrapFlag) {
|
||||
PROTECT(t, loader);
|
||||
PROTECT(t, spec);
|
||||
PROTECT(t, elementClass);
|
||||
|
||||
@ -2014,10 +2016,10 @@ boot(Thread* t)
|
||||
setRoot(t, Machine::StringMap, makeWeakHashMap(t, 0, 0));
|
||||
m->processor->boot(t, 0);
|
||||
|
||||
{ object bootCode = makeCode(t, 0, 0, 0, 0, 0, 1);
|
||||
{ object bootCode = makeCode(t, 0, 0, 0, 0, 0, 0, 1);
|
||||
codeBody(t, bootCode, 0) = impdep1;
|
||||
object bootMethod = makeMethod
|
||||
(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode, 0);
|
||||
(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode);
|
||||
PROTECT(t, bootMethod);
|
||||
|
||||
#include "type-java-initializations.cpp"
|
||||
@ -3217,59 +3219,65 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
{
|
||||
return resolveSystemClass(t, loader, spec, throw_);
|
||||
} else {
|
||||
expect(t, throw_);
|
||||
|
||||
PROTECT(t, loader);
|
||||
PROTECT(t, spec);
|
||||
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
{ ACQUIRE(t, t->m->classLock);
|
||||
|
||||
if (classLoaderMap(t, loader) == 0) {
|
||||
object map = makeHashMap(t, 0, 0);
|
||||
set(t, loader, ClassLoaderMap, map);
|
||||
if (classLoaderMap(t, loader) == 0) {
|
||||
object map = makeHashMap(t, 0, 0);
|
||||
set(t, loader, ClassLoaderMap, map);
|
||||
}
|
||||
|
||||
object class_ = hashMapFind
|
||||
(t, classLoaderMap(t, loader), spec, byteArrayHash, byteArrayEqual);
|
||||
|
||||
if (class_) {
|
||||
return class_;
|
||||
}
|
||||
}
|
||||
|
||||
object class_ = hashMapFind
|
||||
(t, classLoaderMap(t, loader), spec, byteArrayHash, byteArrayEqual);
|
||||
object class_;
|
||||
if (byteArrayBody(t, spec, 0) == '[') {
|
||||
class_ = resolveArrayClass(t, loader, spec, throw_);
|
||||
} else {
|
||||
if (root(t, Machine::LoadClassMethod) == 0) {
|
||||
object m = resolveMethod
|
||||
(t, root(t, Machine::BootLoader), "java/lang/ClassLoader",
|
||||
"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
|
||||
if (class_ == 0) {
|
||||
if (byteArrayBody(t, spec, 0) == '[') {
|
||||
class_ = resolveArrayClass(t, loader, spec, throw_);
|
||||
} else {
|
||||
if (root(t, Machine::LoadClassMethod) == 0) {
|
||||
object m = resolveMethod
|
||||
(t, root(t, Machine::BootLoader), "java/lang/ClassLoader",
|
||||
"loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
if (m) {
|
||||
setRoot(t, Machine::LoadClassMethod, m);
|
||||
|
||||
if (m) {
|
||||
setRoot(t, Machine::LoadClassMethod, m);
|
||||
|
||||
object classLoaderClass = type(t, Machine::ClassLoaderType);
|
||||
object classLoaderClass = type(t, Machine::ClassLoaderType);
|
||||
|
||||
if (classVmFlags(t, classLoaderClass) & BootstrapFlag) {
|
||||
resolveSystemClass
|
||||
(t, root(t, Machine::BootLoader),
|
||||
vm::className(t, classLoaderClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (classVmFlags(t, classLoaderClass) & BootstrapFlag) {
|
||||
resolveSystemClass
|
||||
(t, root(t, Machine::BootLoader),
|
||||
vm::className(t, classLoaderClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
object method = findVirtualMethod
|
||||
(t, root(t, Machine::LoadClassMethod), objectClass(t, loader));
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
object method = findVirtualMethod
|
||||
(t, root(t, Machine::LoadClassMethod), objectClass(t, loader));
|
||||
PROTECT(t, method);
|
||||
|
||||
if (LIKELY(t->exception == 0)) {
|
||||
PROTECT(t, method);
|
||||
RUNTIME_ARRAY(char, s, byteArrayLength(t, spec));
|
||||
replace('/', '.', RUNTIME_ARRAY_BODY(s), reinterpret_cast<char*>
|
||||
(&byteArrayBody(t, spec, 0)));
|
||||
|
||||
RUNTIME_ARRAY(char, s, byteArrayLength(t, spec));
|
||||
replace('/', '.', RUNTIME_ARRAY_BODY(s), reinterpret_cast<char*>
|
||||
(&byteArrayBody(t, spec, 0)));
|
||||
object specString = makeString(t, "%s", s);
|
||||
|
||||
object specString = makeString(t, "%s", s);
|
||||
class_ = t->m->processor->invoke(t, method, loader, specString);
|
||||
|
||||
class_ = t->m->processor->invoke(t, method, loader, specString);
|
||||
|
||||
if (LIKELY(class_ and t->exception == 0)) {
|
||||
class_ = jclassVmClass(t, class_);
|
||||
}
|
||||
if (LIKELY(class_ and t->exception == 0)) {
|
||||
class_ = jclassVmClass(t, class_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3278,6 +3286,8 @@ resolveClass(Thread* t, object loader, object spec, bool throw_)
|
||||
if (class_) {
|
||||
PROTECT(t, class_);
|
||||
|
||||
ACQUIRE(t, t->m->classLock);
|
||||
|
||||
hashMapInsert(t, classLoaderMap(t, loader), spec, class_,
|
||||
byteArrayHash);
|
||||
} else if (throw_ and t->exception == 0) {
|
||||
@ -3659,8 +3669,8 @@ collect(Thread* t, Heap::CollectionType type)
|
||||
ENTER(t, Thread::ExclusiveState);
|
||||
|
||||
#ifdef VM_STRESS
|
||||
bool stress = t->stress;
|
||||
if (not stress) t->stress = true;
|
||||
bool stress = (t->flags |= Thread::StressFlag);
|
||||
if (not stress) atomicOr(&(t->flags), Thread::StressFlag);
|
||||
#endif
|
||||
|
||||
Machine* m = t->m;
|
||||
@ -3681,7 +3691,7 @@ collect(Thread* t, Heap::CollectionType type)
|
||||
m->fixedFootprint = 0;
|
||||
|
||||
#ifdef VM_STRESS
|
||||
if (not stress) t->stress = false;
|
||||
if (not stress) atomicAnd(&(t->flags), ~Thread::StressFlag);
|
||||
#endif
|
||||
|
||||
object f = t->m->finalizeQueue;
|
||||
@ -4005,6 +4015,8 @@ vmPrintTrace(Thread* t)
|
||||
Thread* t;
|
||||
} v(t);
|
||||
|
||||
fprintf(stderr, "debug trace for thread %p\n", t);
|
||||
|
||||
t->m->processor->walkStack(t, &v);
|
||||
}
|
||||
|
||||
@ -4012,24 +4024,24 @@ vmPrintTrace(Thread* t)
|
||||
void*
|
||||
vmAddressFromLine(Thread* t, object m, unsigned line)
|
||||
{
|
||||
object code = methodCode(t, m);
|
||||
printf("code: %p\n", code);
|
||||
object lnt = codeLineNumberTable(t, code);
|
||||
printf("lnt: %p\n", lnt);
|
||||
object code = methodCode(t, m);
|
||||
printf("code: %p\n", code);
|
||||
object lnt = codeLineNumberTable(t, code);
|
||||
printf("lnt: %p\n", lnt);
|
||||
|
||||
if (lnt) {
|
||||
unsigned last = 0;
|
||||
unsigned bottom = 0;
|
||||
unsigned top = lineNumberTableLength(t, lnt);
|
||||
for(unsigned i = bottom; i < top; i++)
|
||||
{
|
||||
LineNumber* ln = lineNumberTableBody(t, lnt, i);
|
||||
if(lineNumberLine(ln) == line)
|
||||
return reinterpret_cast<void*>(lineNumberIp(ln));
|
||||
else if(lineNumberLine(ln) > line)
|
||||
return reinterpret_cast<void*>(last);
|
||||
last = lineNumberIp(ln);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
if (lnt) {
|
||||
unsigned last = 0;
|
||||
unsigned bottom = 0;
|
||||
unsigned top = lineNumberTableLength(t, lnt);
|
||||
for(unsigned i = bottom; i < top; i++)
|
||||
{
|
||||
LineNumber* ln = lineNumberTableBody(t, lnt, i);
|
||||
if(lineNumberLine(ln) == line)
|
||||
return reinterpret_cast<void*>(lineNumberIp(ln));
|
||||
else if(lineNumberLine(ln) > line)
|
||||
return reinterpret_cast<void*>(last);
|
||||
last = lineNumberIp(ln);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -30,6 +30,9 @@
|
||||
|
||||
#define ACQUIRE(t, x) MonitorResource MAKE_NAME(monitorResource_) (t, x)
|
||||
|
||||
#define ACQUIRE_OBJECT(t, x) \
|
||||
ObjectMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
||||
|
||||
#define ACQUIRE_RAW(t, x) RawMonitorResource MAKE_NAME(monitorResource_) (t, x)
|
||||
|
||||
#define ENTER(t, state) StateResource MAKE_NAME(stateResource_) (t, state)
|
||||
@ -1507,20 +1510,19 @@ shutDown(Thread* t);
|
||||
inline void
|
||||
stress(Thread* t)
|
||||
{
|
||||
if ((not t->stress)
|
||||
and (not t->tracing)
|
||||
if ((t->flags & (Thread::StressFlag | Thread::TracingFlag)) == 0
|
||||
and t->state != Thread::NoState
|
||||
and t->state != Thread::IdleState)
|
||||
{
|
||||
t->stress = true;
|
||||
atomicOr(&(t->flags), Thread::StressFlag);
|
||||
|
||||
# ifdef VM_STRESS_MAJOR
|
||||
collect(t, Heap::MajorCollection);
|
||||
collect(t, Heap::MajorCollection);
|
||||
# else // not VM_STRESS_MAJOR
|
||||
collect(t, Heap::MinorCollection);
|
||||
collect(t, Heap::MinorCollection);
|
||||
# endif // not VM_STRESS_MAJOR
|
||||
|
||||
t->stress = false;
|
||||
atomicAnd(&(t->flags), ~Thread::StressFlag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2607,6 +2609,21 @@ monitorNotifyAll(Thread* t, object monitor)
|
||||
while (monitorNotify(t, monitor)) { }
|
||||
}
|
||||
|
||||
class ObjectMonitorResource {
|
||||
public:
|
||||
ObjectMonitorResource(Thread* t, object o): o(o), protector(t, &(this->o)) {
|
||||
monitorAcquire(protector.t, o);
|
||||
}
|
||||
|
||||
~ObjectMonitorResource() {
|
||||
monitorRelease(protector.t, o);
|
||||
}
|
||||
|
||||
private:
|
||||
object o;
|
||||
Thread::SingleProtector protector;
|
||||
};
|
||||
|
||||
object
|
||||
objectMonitor(Thread* t, object o, bool createNew);
|
||||
|
||||
@ -2621,8 +2638,7 @@ acquire(Thread* t, object o)
|
||||
object m = objectMonitor(t, o, true);
|
||||
|
||||
if (DebugMonitors) {
|
||||
fprintf(stderr, "thread %p acquires %p for %x\n",
|
||||
t, m, hash);
|
||||
fprintf(stderr, "thread %p acquires %p for %x\n", t, m, hash);
|
||||
}
|
||||
|
||||
monitorAcquire(t, m);
|
||||
@ -2639,8 +2655,7 @@ release(Thread* t, object o)
|
||||
object m = objectMonitor(t, o, false);
|
||||
|
||||
if (DebugMonitors) {
|
||||
fprintf(stderr, "thread %p releases %p for %x\n",
|
||||
t, m, hash);
|
||||
fprintf(stderr, "thread %p releases %p for %x\n", t, m, hash);
|
||||
}
|
||||
|
||||
monitorRelease(t, m);
|
||||
|
@ -14,6 +14,8 @@
|
||||
# define _XOPEN_SOURCE
|
||||
#endif
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
|
||||
#include "sys/mman.h"
|
||||
#include "sys/types.h"
|
||||
#include "sys/stat.h"
|
||||
@ -287,7 +289,9 @@ class MySystem: public System {
|
||||
owner_ = 0;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
if (time) {
|
||||
// pretend anything greater than one million years (in
|
||||
// milliseconds) is infinity so as to avoid overflow:
|
||||
if (time and time < INT64_C(31536000000000000)) {
|
||||
int64_t then = s->now() + time;
|
||||
timespec ts = { then / 1000, (then % 1000) * 1000 * 1000 };
|
||||
int rv UNUSED = pthread_cond_timedwait
|
||||
|
@ -26,8 +26,7 @@
|
||||
|
||||
(type field avian/VMField)
|
||||
|
||||
(type method avian/VMMethod
|
||||
(intptr_t compiled))
|
||||
(type method avian/VMMethod)
|
||||
|
||||
(type addendum avian/Addendum)
|
||||
|
||||
@ -64,6 +63,7 @@
|
||||
(object pool)
|
||||
(object exceptionHandlerTable)
|
||||
(object lineNumberTable)
|
||||
(intptr_t compiled)
|
||||
(uint16_t maxStack)
|
||||
(uint16_t maxLocals)
|
||||
(array uint8_t body))
|
||||
@ -163,6 +163,7 @@
|
||||
|
||||
(type thread java/lang/Thread
|
||||
(require object sleepLock)
|
||||
(require object interruptLock)
|
||||
(require uint8_t interrupted)
|
||||
(alias peer uint64_t eetop))
|
||||
|
||||
|
12
test/GC.java
12
test/GC.java
@ -1,4 +1,14 @@
|
||||
public class GC {
|
||||
private static final Integer cache[] = new Integer[100];
|
||||
private static final Integer MAX_INT_OBJ = new Integer(Integer.MAX_VALUE);
|
||||
|
||||
private static Integer valueOf(int i) {
|
||||
try {
|
||||
return cache[i];
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
return (i == Integer.MAX_VALUE) ? MAX_INT_OBJ : new Integer(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void small() {
|
||||
for (int i = 0; i < 1024; ++i) {
|
||||
@ -145,6 +155,8 @@ public class GC {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
valueOf(1000);
|
||||
|
||||
Object[] array = new Object[1024 * 1024];
|
||||
array[0] = new Object();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user