From c3256c2874754cbffdcba287fc299e5c427ac5c4 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 3 Feb 2012 12:00:02 -0700 Subject: [PATCH] avoid running out of OS resources due to zombie thread accumulation The bug here is that when a thread exits and becomes a "zombie", the OS resources associated with it are not necessarily released until we actually join and dispose of that thread. Since that only happens during garbage collection, and collection normally only happens in response to heap memory pressure, there's no guarantee that we'll GC frequently enough to clean up zombies promptly and avoid running out of resources. The solution is to force a GC whenever we start a new thread and there are at least N zombies waiting to be disposed, where N=16 for now. --- src/classpath-openjdk.cpp | 12 ++++++++++-- src/machine.cpp | 7 +++++++ src/machine.h | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 2b12362a6c..c2b8471dde 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -3231,12 +3231,20 @@ EXPORT(JVM_DisableCompiler)(Thread*, jclass) // ignore } +uint64_t +jvmStartThread(Thread* t, uintptr_t* arguments) +{ + jobject thread = reinterpret_cast(arguments[0]); + + return startThread(t, *thread) != 0; +} + extern "C" JNIEXPORT void JNICALL EXPORT(JVM_StartThread)(Thread* t, jobject thread) { - ENTER(t, Thread::ActiveState); + uintptr_t arguments[] = { reinterpret_cast(thread) }; - startThread(t, *thread); + run(t, jvmStartThread, arguments); } extern "C" JNIEXPORT void JNICALL diff --git a/src/machine.cpp b/src/machine.cpp index fbf8ca2511..c9026e5360 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -229,6 +229,9 @@ turnOffTheLights(Thread* t) visitAll(t, t->m->rootThread, disposeNoRemove); System* s = m->system; + + expect(s, m->threadCount == 0); + Heap* h = m->heap; Processor* p = m->processor; Classpath* c = m->classpath; @@ -2435,6 +2438,7 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, propertyCount(propertyCount), arguments(arguments), argumentCount(argumentCount), + threadCount(0), activeCount(0), liveCount(0), daemonCount(0), @@ -2653,6 +2657,8 @@ Thread::dispose() } } + -- m->threadCount; + m->heap->free(defaultHeap, ThreadHeapSizeInBytes); m->processor->dispose(this); @@ -2864,6 +2870,7 @@ enter(Thread* t, Thread::State s) INCREMENT(&(t->m->activeCount), 1); if (t->state == Thread::NoState) { ++ t->m->liveCount; + ++ t->m->threadCount; } t->state = s; } break; diff --git a/src/machine.h b/src/machine.h index b11ca98667..96ac9c5086 100644 --- a/src/machine.h +++ b/src/machine.h @@ -120,6 +120,10 @@ const unsigned ThreadHeapPoolSize = 64; const unsigned FixedFootprintThresholdInBytes = ThreadHeapPoolSize * ThreadHeapSizeInBytes; +// number of zombie threads which may accumulate before we force a GC +// to clean them up: +const unsigned ZombieCollectionThreshold = 16; + enum FieldCode { VoidField, ByteField, @@ -1299,6 +1303,7 @@ class Machine { unsigned propertyCount; const char** arguments; unsigned argumentCount; + unsigned threadCount; unsigned activeCount; unsigned liveCount; unsigned daemonCount; @@ -1994,6 +1999,7 @@ addThread(Thread* t, Thread* p) assert(t, p->state == Thread::NoState); p->state = Thread::IdleState; + ++ t->m->threadCount; ++ t->m->liveCount; p->peer = p->parent->child; @@ -2012,6 +2018,7 @@ removeThread(Thread* t, Thread* p) assert(t, p->state == Thread::IdleState); -- t->m->liveCount; + -- t->m->threadCount; t->m->stateLock->notifyAll(t->systemThread); @@ -2020,11 +2027,25 @@ removeThread(Thread* t, Thread* p) if (p->javaThread) { threadPeer(t, p->javaThread) = 0; } + + p->dispose(); } inline Thread* startThread(Thread* t, object javaThread) { + { PROTECT(t, javaThread); + + stress(t); + + ACQUIRE_RAW(t, t->m->stateLock); + + if (t->m->threadCount > t->m->liveCount + ZombieCollectionThreshold) { + fprintf(stderr, "hit zombie collection threshold\n"); + collect(t, Heap::MinorCollection); + } + } + Thread* p = t->m->processor->makeThread(t->m, javaThread, t); addThread(t, p);