home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
kaffe-0.5p4-src.tgz
/
tar.out
/
contrib
/
kaffe
/
kaffevm
/
gc-simple.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-09-28
|
11KB
|
509 lines
/*
* gc-simple.c
* The garbage collector.
*
* Copyright (c) 1996 Systems Architecture Research Centre,
* City University, London, UK.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* Written by Tim Wilkinson <tim@sarc.city.ac.uk>, February 1996.
*/
/* #define NOGC /* Turn off garbage collection */
#define DBG(s)
#define MDBG(s)
#define FDBG(s)
#define ADBG(s)
#define RDBG(s)
#define TDBG(s)
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#if defined(HAVE_MALLOC_H)
#include <malloc.h>
#endif
#include "gtypes.h"
#include "access.h"
#include "object.h"
#include "constants.h"
#include "classMethod.h"
#include "baseClasses.h"
#include "lookup.h"
#include "thread.h"
#include "gc.h"
#include "locks.h"
#include "slots.h"
#include "external.h"
#include "errors.h"
#include "machine.h"
#include "md.h"
#include "flags.h"
extern classes* ThreadClass;
extern strpair* finalpair;
extern thread* finalman;
extern thread* garbageman;
extern thread* liveThreads;
extern thread* currentThread;
static gcRef* refTable[GCREFTABLESZ];
static int refTableIdx;
static gcRef* freeRef;
static gcRef* permList;
static void* minGcMemory = (void*)-1;
static void* maxGcMemory = (void*)0;
static gcRef* garbageObjects;
static gcRef* walkTail;
static struct {
int mark;
int total;
int64 time;
} gcStats;
static void markObject(object*);
static void walkObject(gcRef*);
static void garbageCollect(void);
static gcRef* validReference(object*);
extern int64 currentTime(void);
/*
* Initialise garbage system.
*/
void
initGc(void)
{
/* Does nothing */
}
/*
* Primitive object allocation.
*/
void*
newObject(int sz, classes* class, int arraysz, bool perm)
{
static uintp allocSize = 0;
object* obj;
gcRef* table;
gcRef* ref;
int i;
sz += sizeof(object);
ADBG( printf("newObject size %d\n", sz); )
obj = (object*)calloc(sz, sizeof(char));
if (obj == 0) {
throwException(OutOfMemoryError);
}
/* Invoke garbage collection when necessary */
allocSize += sz;
if (allocSize > GCTRIGSIZE) {
allocSize = 0;
invokeGarbageCollector();
}
/* Fill in object information */
obj->size = arraysz;
obj->dtable = (class != 0 ? class->dtable : 0);
#if defined(NOGC)
return (obj);
#endif
/* Reset min and max memory pointers */
if ((void*)obj < minGcMemory) {
minGcMemory = (void*)obj;
}
if ((void*)obj > maxGcMemory) {
maxGcMemory = (void*)obj;
}
ref = freeRef;
if (ref != 0) {
freeRef = freeRef->next;
}
else {
assert(refTableIdx < GCREFTABLESZ);
table = malloc(sizeof(gcRef) * GCREFMAX);
assert(table != 0);
/* Build into free list */
for (i = 0; i < GCREFMAX; i++) {
table[i].flags = GC_FREE;
table[i].next = &table[i+1];
table[i].idx = i + (refTableIdx * GCREFMAX);
}
table[GCREFMAX-1].next = 0;
freeRef = &table[1];
ref = &table[0];
refTable[refTableIdx] = table;
refTableIdx++;
}
assert(ref->flags == GC_FREE);
/* Make ref point at object, and object point at ref */
obj->gc.idx = ref->idx;
ref->obj = obj;
ref->flags = GC_UNMARK;
ref->next = 0;
/* If object is permenant, put it on the perm list */
if (perm == true) {
ref->next = permList;
permList = ref;
}
gcStats.total++;
RDBG( printf("Adding new object 0x%x at index %d - %s\n", obj, obj->gc.idx, obj->dtable ? obj->dtable->class->name : "<none>"); )
return (obj);
}
/*
* Invoke the garbage collector (if we have one)
*/
void
invokeGarbageCollector(void)
{
#if defined(NOGC)
return;
#endif
if (garbageman != 0) {
lockMutex(&garbageman->obj);
signalCond(&garbageman->obj);
unlockMutex(&garbageman->obj);
}
}
/*
* Is object reference a real object?
*/
static
gcRef*
validReference(object* o)
{
gcRef* ref;
/* Null references are not objects */
if (o == 0) {
return (0);
}
/* Validate object address. To be a real object it must lie
inside the malloced memory pool, and its index must refer to
a GC entry which refers to it. */
if ((void*)o < minGcMemory || (void*)o > maxGcMemory) {
return (0);
}
/* Check alignment - should be pointer aligned */
if (((uintp)o) % sizeof(void*) != 0) {
return (0);
}
ref = refTable[(o->gc.idx / GCREFMAX) % GCREFTABLESZ];
if (ref == 0) {
return (0);
}
ref = &ref[o->gc.idx % GCREFMAX];
if (ref->obj != o) {
return (0);
}
/*
* If object is unmarked, then we should return it and
* let the system recurse though it. If not, we should
* return 0 so the system will not recurse.
*/
if (ref->flags == GC_UNMARK || ref->flags == GC_UNMARK2) {
return (ref);
}
else {
return (0);
}
}
/*
* Garbage collect objects.
*/
static
void
garbageCollect(void)
{
int i;
thread* tid;
object** ptr;
gcRef* ref;
int j;
gcRef* table;
int64 tms;
int64 tme;
#if defined(NOGC)
return;
#endif
DBG( printf("Garbage collecting ... \n"); )
tms = currentTime();
gcStats.mark = 0;
/* Scan the permentant object list */
assert(permList != 0);
walkTail = permList;
walkTail->flags = GC_MARK;
for (ref = permList->next; ref != 0; ref = ref->next) {
markObject(ref->obj);
}
/* Scan each live thread */
for (tid = liveThreads; tid != 0; tid = tid->PrivateInfo->nextlive) {
markObject(&tid->obj);
for (ptr = (object**)tid->PrivateInfo->restorePoint; ptr < (object**)tid->PrivateInfo->stackEnd; ptr++) {
markObject(*ptr);
}
}
/* Now scan walk list - which will add more to the walk list - until
* it is exausted.
*/
for (ref = permList; ref != walkTail; ref = ref->walk) {
walkObject(ref);
}
walkObject(ref); /* And the last one */
/* Look through object table for unmarked objects which must be
* finalises. Attach them to the garbage list and then reference
* them (so any objects the reference are not garbaged).
*/
for (j = 0; j < refTableIdx; j++) {
table = refTable[j];
for (i = 0; i < GCREFMAX; i++) {
if (table[i].flags == GC_UNMARK &&
table[i].obj->dtable->class->final == true) {
markObject(table[i].obj);
table[i].flags = GC_GARBAGE;
table[i].next = garbageObjects;
garbageObjects = &table[i];
}
}
}
/* Continue to scan walk list until it is exausted again. We've
* already done the current 'ref' so skip it.
*/
while (ref != walkTail) {
ref = ref->walk;
walkObject(ref);
}
/* Now look through object table for objects which have not
been marked. These can be freed. */
for (j = 0; j < refTableIdx; j++) {
table = refTable[j];
for (i = 0; i < GCREFMAX; i++) {
switch(table[i].flags) {
case GC_GARBAGE:
case GC_GARBAGE2:
case GC_FREE:
break;
case GC_MARK:
table[i].flags = GC_UNMARK; /* For next time */
break;
case GC_UNMARK:
assert(table[i].obj->dtable->class->final == false);
table[i].flags = GC_GARBAGE2;
table[i].next = garbageObjects;
garbageObjects = &table[i];
break;
case GC_UNMARK2:
/* Object not marked - free */
table[i].flags = GC_GARBAGE2;
table[i].next = garbageObjects;
garbageObjects = &table[i];
break;
}
}
}
DBG( printf("Garbage collecting ... done.\n"); )
if (flag_gc) {
tme = currentTime();
gcStats.time += (tme - tms);
fprintf(stderr, "<GC: total %d, marked %d, freeing %d, time %dms (%dms)>\n", gcStats.total, gcStats.mark, gcStats.total - gcStats.mark, (int)(tme - tms), (int)gcStats.time);
}
}
/*
* Given an object, mark it and follow any object references it contains.
*/
static
void
markObject(object* o)
{
gcRef* ref;
/* Null object, invalid object or already visited? */
ref = validReference(o);
if (ref == 0) {
return;
}
MDBG( printf("Marking object 0x%x at index %d - %s\n", o, o->gc.idx, o->dtable ? o->dtable->class->name : "<none>"); )
/* Mark and add to walk list */
walkTail->walk = ref;
walkTail = ref;
ref->flags = GC_MARK;
gcStats.mark++;
}
static
void
walkObject(gcRef* ref)
{
int i;
classes* class;
object** objdata;
class = ref->obj->dtable->class;
/* Mark the class object */
markObject(&class->head);
/* If this is a class object, mark the static fields and constants */
if (class == ClassClass) {
class = (classes*)ref->obj;
markObject(&class->superclass->head);
objdata = (object**)class->staticFields;
for (i = 0; i < class->sfsize; i++) {
markObject(objdata[i]);
}
if (class->constants != 0) {
objdata = (object**)class->constants->data;
for (i = class->constants->size - 1; i >= 0; i--) {
markObject(objdata[i]);
}
}
}
/* Otherwise, if a normal object ... */
else if (class->sig[0] == 'L') {
objdata = (object**)(ref->obj+1);
for (i = 0; i < class->fsize; i++) {
markObject(objdata[i]);
}
}
/* Otherwise, if a array of objects ... */
else if (class->sig[0] == '[' &&
(class->sig[1] == 'L' || class->sig[1] == '[')) {
objdata = (object**)(ref->obj+1);
for (i = 0; i < ref->obj->size; i++) {
markObject(objdata[i]);
}
}
}
/*
* Finaliser.
* Finalises any objects which have been garbage collected before
* deleting them.
*/
void
finaliserMan(void)
{
object* obj;
gcRef* ref;
methods* meth;
/* All threads start with interrupts disabled */
intsRestore();
lockMutex(¤tThread->obj);
for (;;) {
while (garbageObjects) {
ref = garbageObjects;
garbageObjects = garbageObjects->next;
assert(ref->flags == GC_GARBAGE || ref->flags == GC_GARBAGE2);
obj = ref->obj;
/* Finalise object if necessary */
if (ref->flags == GC_GARBAGE) {
meth = findMethod(obj->dtable->class, finalpair);
assert(meth != 0);
FDBG( printf("Finalise object 0x%x(%d) %s\n", obj,
ref->idx, obj->dtable->class->name); )
CALL_KAFFE_FUNCTION(meth, obj);
/* Don't free it cause it might live */
ref->flags = GC_UNMARK2;
ref->next = 0;
continue;
}
/* Special handling for threads */
if (obj->dtable->class == ThreadClass) {
free(((thread*)obj)->PrivateInfo);
TDBG( printf("Freeing thread 0x%x(%d)\n",
obj, ref->idx); )
}
/* Don't handle classes yet */
assert(obj->dtable->class != ClassClass);
/* Put entry onto freelist */
ref->flags = GC_FREE;
ref->next = freeRef;
freeRef = ref;
/* Free the object */
FDBG( printf("Freeing object 0x%x(%d) %s\n", obj,
ref->idx, obj->dtable->class->name); )
free(obj);
gcStats.total--;
}
waitCond(¤tThread->obj, waitforever);
}
}
/*
* Garbage collector.
* Run the garbage collector.
*/
void
gcMan(void)
{
/* All threads start with interrupts disabled */
intsRestore();
lockMutex(¤tThread->obj);
for (;;) {
waitCond(¤tThread->obj, waitforever);
/* Run the garbage collector */
garbageCollect();
/* If there's garbage, finalise it */
if (garbageObjects != 0 && finalman != 0) {
lockMutex(&finalman->obj);
signalCond(&finalman->obj);
unlockMutex(&finalman->obj);
}
}
}