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.
This commit is contained in:
Joel Dice 2009-07-21 18:57:55 -06:00
parent 514d0bf7e5
commit 3787985b25
5 changed files with 113 additions and 32 deletions

View File

@ -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<uintptr_t>(makeNewWeakReference(t, class_));
return reinterpret_cast<uintptr_t>(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,

View File

@ -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<const int8_t*>("finalize"),
&byteArrayBody(t, methodName(t, m), 0)) == 0
and strcmp(reinterpret_cast<const int8_t*>("()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<const int8_t*>("finalize"),
&byteArrayBody(t, methodName(t, method), 0)) == 0
and strcmp
(reinterpret_cast<const int8_t*>("()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);

View File

@ -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);

View File

@ -38,7 +38,7 @@ THUNK(makeMultidimensionalArray)
THUNK(throw_)
THUNK(checkCast)
THUNK(instanceOf64)
THUNK(makeNewWeakReference64)
THUNK(makeNewGeneral64)
THUNK(makeNew64)
THUNK(set)
THUNK(gcIfNecessary)

34
test/Finalizers.java Normal file
View File

@ -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 { }
}