From 3787985b25f81619dacc8b4d7ed7ac98cc57ab55 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 21 Jul 2009 18:57:55 -0600 Subject: [PATCH] implement basic finalization support This implementation does not conform to the Java standard in that finalize methods are called from whichever thread happens to be garbage collecting, and that thread may hold locks, whereas the standard guarantees that finalize will be run from a thread which holds no locks. Also, an object will never be finalized more than once, even if its finalize method "rescues" (i.e. makes reachable) the object such that it might become unreachable a second time and thus a candidate for finalization once more. It's not clear to me from the standard if this is OK or not. Nonwithstanding the above, this implementation is useful for "normal" finalize methods which simply release resources associated with an object. --- src/compile.cpp | 16 +++--------- src/machine.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++- src/machine.h | 34 ++++++++++++------------- src/thunks.cpp | 2 +- test/Finalizers.java | 34 +++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 32 deletions(-) create mode 100644 test/Finalizers.java diff --git a/src/compile.cpp b/src/compile.cpp index f03e5a843d..869a75a00a 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -2247,9 +2247,9 @@ instanceOf64(Thread* t, object class_, object o) } uint64_t -makeNewWeakReference64(Thread* t, object class_) +makeNewGeneral64(Thread* t, object class_) { - return reinterpret_cast(makeNewWeakReference(t, class_)); + return reinterpret_cast(makeNewGeneral(t, class_)); } uint64_t @@ -2318,14 +2318,6 @@ pushReturnValue(MyThread* t, Frame* frame, unsigned code, } } -bool -emptyMethod(MyThread* t, object method) -{ - return ((methodFlags(t, method) & ACC_NATIVE) == 0) - and (codeLength(t, methodCode(t, method)) == 1) - and (codeBody(t, methodCode(t, method), 0) == return_); -} - Compiler::Operand* compileDirectInvoke(MyThread* t, Frame* frame, object target, bool tailCall, bool useThunk, unsigned rSize, Promise* addressPromise) @@ -3967,10 +3959,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, object class_ = resolveClassInPool(t, codePool(t, code), index - 1); if (UNLIKELY(t->exception)) return; - if (classVmFlags(t, class_) & WeakReferenceFlag) { + if (classVmFlags(t, class_) & (WeakReferenceFlag | HasFinalizerFlag)) { frame->pushObject (c->call - (c->constant(getThunk(t, makeNewWeakReference64Thunk)), + (c->constant(getThunk(t, makeNewGeneral64Thunk)), 0, frame->trace(0, 0), BytesPerWord, diff --git a/src/machine.cpp b/src/machine.cpp index 1b4e418b54..3946e8f647 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -481,6 +481,27 @@ postCollect(Thread* t) } } +void +finalizeObject(Thread* t, object o) +{ + for (object c = objectClass(t, o); c; c = classSuper(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object m = arrayBody(t, classMethodTable(t, c), i); + + if (strcmp(reinterpret_cast("finalize"), + &byteArrayBody(t, methodName(t, m), 0)) == 0 + and strcmp(reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, m), 0)) == 0) + { + t->m->processor->invoke(t, m, o); + t->exception = 0; + return; + } + } + } + abort(t); +} + object makeByteArray(Thread* t, const char* format, va_list a) { @@ -1247,6 +1268,17 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) hashMapInsert(t, virtualMap, method, method, methodHash); } + + if (UNLIKELY(strcmp + (reinterpret_cast("finalize"), + &byteArrayBody(t, methodName(t, method), 0)) == 0 + and strcmp + (reinterpret_cast("()V"), + &byteArrayBody(t, methodSpec(t, method), 0)) == 0 + and (not emptyMethod(t, method)))) + { + classVmFlags(t, class_) |= HasFinalizerFlag; + } } else { methodOffset(t, method) = i; @@ -1401,6 +1433,8 @@ updateBootstrapClass(Thread* t, object bootstrapClass, object class_) expect(t, bootstrapClass == arrayBody(t, t->m->types, Machine::ClassType) or classFixedSize(t, bootstrapClass) >= classFixedSize(t, class_)); + expect(t, (classVmFlags(t, class_) & HasFinalizerFlag) == 0); + PROTECT(t, bootstrapClass); PROTECT(t, class_); @@ -2232,6 +2266,28 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, } } +object +makeNewGeneral(Thread* t, object class_) +{ + assert(t, t->state == Thread::ActiveState); + + object instance = makeNew(t, class_); + PROTECT(t, instance); + + if (classVmFlags(t, class_) & WeakReferenceFlag) { + ACQUIRE(t, t->m->referenceLock); + + jreferenceVmNext(t, instance) = t->m->weakReferences; + t->m->weakReferences = instance; + } + + if (classVmFlags(t, class_) & HasFinalizerFlag) { + addFinalizer(t, instance, finalizeObject); + } + + return instance; +} + object makeByteArray(Thread* t, const char* format, ...) { @@ -2498,7 +2554,8 @@ parseClass(Thread* t, const uint8_t* data, unsigned size) set(t, class_, ClassSuper, sc); classVmFlags(t, class_) - |= (classVmFlags(t, sc) & (ReferenceFlag | WeakReferenceFlag)); + |= (classVmFlags(t, sc) + & (ReferenceFlag | WeakReferenceFlag | HasFinalizerFlag)); } parseInterfaceTable(t, s, class_, pool); diff --git a/src/machine.h b/src/machine.h index 6d5ed41004..bd0a582f22 100644 --- a/src/machine.h +++ b/src/machine.h @@ -76,6 +76,7 @@ const int UnknownLine = -2; // class flags (note that we must be careful not to overlap the // standard ACC_* flags): +const unsigned HasFinalMemberFlag = 1 << 13; const unsigned SingletonFlag = 1 << 14; const unsigned ContinuationFlag = 1 << 15; @@ -87,7 +88,7 @@ const unsigned InitFlag = 1 << 3; const unsigned InitErrorFlag = 1 << 4; const unsigned PrimitiveFlag = 1 << 5; const unsigned BootstrapFlag = 1 << 6; -const unsigned HasFinalMemberFlag = 1 << 7; +const unsigned HasFinalizerFlag = 1 << 7; // method vmFlags: const unsigned ClassInitFlag = 1 << 0; @@ -1821,27 +1822,16 @@ makeNew(Thread* t, object class_) return instance; } -inline object -makeNewWeakReference(Thread* t, object class_) -{ - assert(t, t->state == Thread::ActiveState); - - object instance = makeNew(t, class_); - PROTECT(t, instance); - - ACQUIRE(t, t->m->referenceLock); - - jreferenceVmNext(t, instance) = t->m->weakReferences; - t->m->weakReferences = instance; - - return instance; -} +object +makeNewGeneral(Thread* t, object class_); inline object make(Thread* t, object class_) { - if (UNLIKELY(classVmFlags(t, class_) & WeakReferenceFlag)) { - return makeNewWeakReference(t, class_); + if (UNLIKELY(classVmFlags(t, class_) + & (WeakReferenceFlag | HasFinalizerFlag))) + { + return makeNewGeneral(t, class_); } else { return makeNew(t, class_); } @@ -2082,6 +2072,14 @@ fieldSize(Thread* t, object field) object findLoadedClass(Thread* t, object spec); +inline bool +emptyMethod(Thread* t, object method) +{ + return ((methodFlags(t, method) & ACC_NATIVE) == 0) + and (codeLength(t, methodCode(t, method)) == 1) + and (codeBody(t, methodCode(t, method), 0) == return_); +} + object parseClass(Thread* t, const uint8_t* data, unsigned length); diff --git a/src/thunks.cpp b/src/thunks.cpp index cdb37c4cc6..bb1721ffe1 100644 --- a/src/thunks.cpp +++ b/src/thunks.cpp @@ -38,7 +38,7 @@ THUNK(makeMultidimensionalArray) THUNK(throw_) THUNK(checkCast) THUNK(instanceOf64) -THUNK(makeNewWeakReference64) +THUNK(makeNewGeneral64) THUNK(makeNew64) THUNK(set) THUNK(gcIfNecessary) diff --git a/test/Finalizers.java b/test/Finalizers.java new file mode 100644 index 0000000000..b7844988bf --- /dev/null +++ b/test/Finalizers.java @@ -0,0 +1,34 @@ +public class Finalizers { + private static boolean finalized = false; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + protected void finalize() { + finalized = true; + } + + public static void main(String[] args) { + new Finalizers(); + + expect(! finalized); + + System.gc(); + + expect(finalized); + + new Finalizers2(); + + finalized = false; + + expect(! finalized); + + System.gc(); + + expect(finalized); + } + + private static class Finalizers2 extends Finalizers { } + +}