Garbage collection:

Moved from reference counting over to a mark and sweep method.  Objects
are now required to implement an allRefs method which returns a pointer
to a list of all other referenced objects.  How the list is maintained
is up to each object.  The retain/release mechanism still works; objects
with any references will not be garbage collected, even if the collector
thinks they should be.
This commit is contained in:
Brian Koropoff 2003-12-09 02:01:05 +00:00
parent e498b196fe
commit 991fc06360
8 changed files with 121 additions and 58 deletions

View file

@ -37,6 +37,7 @@
classDecl (ArrayList, List, classDecl (ArrayList, List,
unsigned int realsize; unsigned int realsize;
Object **elements; Object **elements;
ObjRefs_t allrefs;
); );
#define ARRAYLIST(o) ((ArrayList *)(o)) #define ARRAYLIST(o) ((ArrayList *)(o))
@ -45,6 +46,7 @@ classDecl (ArrayListIterator, Iterator,
unsigned int pos; unsigned int pos;
unsigned int smods; unsigned int smods;
qboolean alive; qboolean alive;
ObjRefs_t allrefs;
); );
#define ARRAYLISTITERATOR(o) ((ArrayListIterator *)(o)) #define ARRAYLISTITERATOR(o) ((ArrayListIterator *)(o))

View file

@ -61,6 +61,12 @@
#define instanceOf(obj, cl) (Object_InstaceOf((Object *)obj, cl##_class)) #define instanceOf(obj, cl) (Object_InstaceOf((Object *)obj, cl##_class))
typedef struct ObjRefs_s {
struct Object_s **objs;
unsigned int count;
struct ObjRefs_s *next;
} ObjRefs_t;
struct Object_s; struct Object_s;
struct Class_s; struct Class_s;
struct List_s; struct List_s;
@ -70,10 +76,10 @@ typedef void (*ReplyHandler_t) (struct Object_s *retValue);
typedef struct Object_s { typedef struct Object_s {
struct Class_s *cl; struct Class_s *cl;
int refs; int refs;
qboolean junked; qboolean marked;
struct Object_s *next; struct Object_s *next;
struct String_s * methodDecl(Object, toString); struct String_s * methodDecl(Object, toString);
void methodDecl(Object, message, const char *name, struct List_s *args, struct Object_s *sender, ReplyHandler_t *reply); ObjRefs_t * methodDecl(Object, allRefs);
void *data; void *data;
} Object; } Object;
@ -99,6 +105,8 @@ void Object_Delete (Object *obj);
Object *Object_Retain (Object *obj); Object *Object_Retain (Object *obj);
Object *Object_Release (Object *obj); Object *Object_Release (Object *obj);
qboolean Object_InstanceOf (Object *obj, Class *cl); qboolean Object_InstanceOf (Object *obj, Class *cl);
void Object_AddToRoot (Object *obj);
void Object_RemoveFromRoot (Object *obj);
void Object_Init (void); void Object_Init (void);
void Object_Garbage_Collect (void); void Object_Garbage_Collect (void);

View file

@ -32,8 +32,9 @@
#ifndef __garbage_h #ifndef __garbage_h
#define __garbage_h #define __garbage_h
void Garbage_Junk_Object (Object *o); void Garbage_Do_Mark (Object *root);
unsigned int Garbage_Amount (void); void Garbage_Do_Sweep (Object **allobjs);
void Garbage_Collect (unsigned int amount); unsigned int Garbage_Pending (void);
void Garbage_Dispose (unsigned int amount);
#endif #endif

View file

@ -46,6 +46,12 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "QF/classes/ArrayList.h" #include "QF/classes/ArrayList.h"
static ObjRefs_t *
ArrayListIterator_AllRefs_f (Object *self)
{
return &ARRAYLISTITERATOR(self)->allrefs;
}
static Object * static Object *
ArrayListIterator_Next_f (Iterator *self) ArrayListIterator_Next_f (Iterator *self)
{ {
@ -75,9 +81,13 @@ static Object *
ArrayListIterator_Init_f (Object *self, ArrayList *list) ArrayListIterator_Init_f (Object *self, ArrayList *list)
{ {
superInit (ArrayListIterator, self); superInit (ArrayListIterator, self);
self->allRefs = ArrayListIterator_AllRefs_f;
ITERATOR(self)->next = ArrayListIterator_Next_f; ITERATOR(self)->next = ArrayListIterator_Next_f;
ITERATOR(self)->hasNext = ArrayListIterator_HasNext_f; ITERATOR(self)->hasNext = ArrayListIterator_HasNext_f;
ARRAYLISTITERATOR(self)->list = ARRAYLIST(retain(list)); ARRAYLISTITERATOR(self)->list = ARRAYLIST(list);
ARRAYLISTITERATOR(self)->allrefs.objs = (Object **)&ARRAYLISTITERATOR(self)->list;
ARRAYLISTITERATOR(self)->allrefs.count = 1;
ARRAYLISTITERATOR(self)->allrefs.next = NULL;
ARRAYLISTITERATOR(self)->smods = LIST(list)->smods; ARRAYLISTITERATOR(self)->smods = LIST(list)->smods;
ARRAYLISTITERATOR(self)->pos = 0; ARRAYLISTITERATOR(self)->pos = 0;
ARRAYLISTITERATOR(self)->alive = COLLECTION(list)->count ? true : false; ARRAYLISTITERATOR(self)->alive = COLLECTION(list)->count ? true : false;
@ -87,7 +97,15 @@ ArrayListIterator_Init_f (Object *self, ArrayList *list)
static void static void
ArrayListIterator_Deinit_f (Object *self) ArrayListIterator_Deinit_f (Object *self)
{ {
release(ARRAYLISTITERATOR(self)->list); }
static ObjRefs_t *
ArrayList_AllRefs_f (Object *self)
{
ARRAYLIST(self)->allrefs.objs = ARRAYLIST(self)->elements;
ARRAYLIST(self)->allrefs.count = COLLECTION(self)->count;
ARRAYLIST(self)->allrefs.next = NULL;
return &ARRAYLIST(self)->allrefs;
} }
static void static void
@ -109,13 +127,8 @@ ArrayList_Set_f (List *self, unsigned int index, Object *o)
if (!Object_InstanceOf (o, COLLECTION(list)->type)) if (!Object_InstanceOf (o, COLLECTION(list)->type))
return false; return false;
ArrayList_EnsureCapacity (list, index+1); ArrayList_EnsureCapacity (list, index+1);
retain(o);
if (list->elements[index])
release (list->elements[index]);
list->elements[index] = o; list->elements[index] = o;
} else if (index < COLLECTION(list)->count) { } else if (index < COLLECTION(list)->count) {
if (list->elements[index])
release (list->elements[index]);
list->elements[index] = NULL; list->elements[index] = NULL;
} }
if (COLLECTION(list)->count < index+1) if (COLLECTION(list)->count < index+1)
@ -151,7 +164,6 @@ ArrayList_InsertAt_f (List *self, unsigned int index, Object *o)
return false; return false;
else { else {
ArrayList_MakeRoomAt (list, index); ArrayList_MakeRoomAt (list, index);
retain(o);
list->elements[index] = o; list->elements[index] = o;
return true; return true;
} }
@ -165,8 +177,6 @@ ArrayList_RemoveAt_f (List *self, unsigned int index)
return NULL; return NULL;
else { else {
Object *o = list->elements[index]; Object *o = list->elements[index];
if (o)
release(o);
COLLECTION(list)->count--; COLLECTION(list)->count--;
memmove(list->elements+index, list->elements+index+1, sizeof(Object *) * (COLLECTION(list)->count-index)); memmove(list->elements+index, list->elements+index+1, sizeof(Object *) * (COLLECTION(list)->count-index));
LIST(list)->smods++; LIST(list)->smods++;
@ -183,8 +193,6 @@ ArrayList_Add_f (Collection *self, Object *o)
else { else {
ArrayList_EnsureCapacity (list, ++COLLECTION(list)->count); ArrayList_EnsureCapacity (list, ++COLLECTION(list)->count);
list->elements[COLLECTION(list)->count-1] = o; list->elements[COLLECTION(list)->count-1] = o;
if (o)
retain(o);
return true; return true;
} }
} }
@ -207,16 +215,13 @@ ArrayList_Init_f (Object *self, Class *type, Collection *source)
COLLECTION(self)->add = ArrayList_Add_f; COLLECTION(self)->add = ArrayList_Add_f;
COLLECTION(self)->iterator = ArrayList_Iterator_f; COLLECTION(self)->iterator = ArrayList_Iterator_f;
superInit(List, self, type, source); superInit(List, self, type, source);
self->allRefs = ArrayList_AllRefs_f;
return self; return self;
} }
static void static void
ArrayList_Deinit_f (Object *self) ArrayList_Deinit_f (Object *self)
{ {
unsigned int i;
for (i = 0; i < COLLECTION(self)->count; i++)
if (ARRAYLIST(self)->elements[i])
release(ARRAYLIST(self)->elements[i]);
free(ARRAYLIST(self)->elements); free(ARRAYLIST(self)->elements);
} }

View file

@ -43,7 +43,7 @@ static __attribute__ ((unused)) const char rcsid[] =
static String * static String *
Double_ToString_f (Object *self) Double_ToString_f (Object *self)
{ {
return new (String, va("%f", DOUBLE(self)->value)); return newFloat(String, va("%f", DOUBLE(self)->value));
} }
static int static int

View file

@ -42,48 +42,61 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "garbage.h" #include "garbage.h"
unsigned int junked = 0;
Object *junk = NULL; Object *junk = NULL;
unsigned int junked = 0;
void void
Garbage_Junk_Object (Object *o) Garbage_Do_Mark (Object *root)
{ {
Sys_DPrintf ("GC: %s@%p ready for collection.\n", o->cl->name, o); if (!root->marked) {
if (!o->junked && !o->refs) { ObjRefs_t *allrefs;
o->next = junk; root->marked = true;
junk = o; Sys_DPrintf ("GC: Marked %s@%p.\n", root->cl->name, root);
o->junked = true; if (root->allRefs)
for (allrefs = methodCall(root, allRefs); allrefs; allrefs = allrefs->next) {
unsigned int i;
for (i = 0; i < allrefs->count; i++)
Garbage_Do_Mark (allrefs->objs[i]);
}
}
}
void
Garbage_Do_Sweep (Object **allobjs)
{
Object **prevNext;
Object *obj;
for (prevNext = allobjs, obj = *allobjs; obj; obj = *prevNext) {
if (obj->marked) {
obj->marked = false;
prevNext = &obj->next;
} else if (!obj->refs) {
*prevNext = obj->next;
obj->next = junk;
junk = obj;
junked++; junked++;
Sys_DPrintf ("GC: %s@%p is ready for disposal...\n", obj->cl->name, obj);
} else
*prevNext = obj->next;
} }
} }
unsigned int unsigned int
Garbage_Amount (void) Garbage_Pending (void)
{ {
return junked; return junked;
} }
void void
Garbage_Collect (unsigned int amount) Garbage_Dispose (unsigned int amount)
{ {
Object *o; Object *next;
if (!amount) for (; junk && amount; junk = next, amount--) {
return; next = junk->next;
Sys_DPrintf ("GC: Collecting %u objects...\n", amount); Sys_DPrintf ("GC: Disposing of %s@%p...\n", junk->cl->name, junk);
Object_Delete (junk);
for (o = junk; o && amount; o = junk) {
junk = o->next;
if (!o->refs) {
Sys_DPrintf("GC: Collecting %s@%p...\n", o->cl->name, o);
Object_Delete (o);
amount--;
} else {
Sys_DPrintf("GC: %s@%p gained references, not collecting...\n", o->cl->name, o);
o->junked = false;
}
junked--; junked--;
} }
junk = o;
} }

View file

@ -58,6 +58,9 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "QF/classes/Integer.h" #include "QF/classes/Integer.h"
#include "QF/classes/Double.h" #include "QF/classes/Double.h"
Object *allObjs = NULL;
ArrayList *rootObj = NULL;
static String * static String *
Object_ToString_f (Object *self) Object_ToString_f (Object *self)
{ {
@ -67,6 +70,7 @@ Object_ToString_f (Object *self)
static void static void
Object_Init_f (Object *self) Object_Init_f (Object *self)
{ {
self->allRefs = NULL;
self->toString = Object_ToString_f; self->toString = Object_ToString_f;
Sys_DPrintf("%s@%p initing...\n", self->cl->name, self); Sys_DPrintf("%s@%p initing...\n", self->cl->name, self);
} }
@ -108,12 +112,13 @@ Object_Create (Class *cl, qboolean floating)
{ {
Object *new = malloc (cl->size); Object *new = malloc (cl->size);
new->cl = cl; new->cl = cl;
new->junked = false;
if (floating) { if (floating) {
new->refs = 0; new->refs = 0;
Garbage_Junk_Object (new);
} else } else
new->refs = 1; new->refs = 1;
new->marked = false;
new->next = allObjs;
allObjs = new;
return new; return new;
} }
@ -135,8 +140,8 @@ Object_Retain (Object *obj)
Object * Object *
Object_Release (Object *obj) Object_Release (Object *obj)
{ {
if (obj->refs && --obj->refs == 0) if (obj->refs)
Garbage_Junk_Object (obj); obj->refs--;
return obj; return obj;
} }
@ -150,6 +155,18 @@ Object_InstanceOf (Object *obj, Class *cl)
return false; return false;
} }
void
Object_AddToRoot (Object *obj)
{
methodCall(COLLECTION(rootObj), add, obj);
}
void
Object_RemoveFromRoot (Object *obj)
{
methodCall(COLLECTION(rootObj), remove, obj);
}
static void static void
Object_Test (void) Object_Test (void)
{ {
@ -169,6 +186,12 @@ Object_Test (void)
methodCall(LIST(list), insertAt, 2, newFloat(String, "Mr. Two!")); methodCall(LIST(list), insertAt, 2, newFloat(String, "Mr. Two!"));
liststr = methodCall(OBJECT(list), toString); liststr = methodCall(OBJECT(list), toString);
Sys_DPrintf("List: %s\n", liststr->str); Sys_DPrintf("List: %s\n", liststr->str);
list = newFloat(ArrayList, classObj(Object), NULL);
methodCall(list, add, newFloat(String, "Don't free me!"));
methodCall(list, add, newFloat(Integer, 5));
methodCall(list, add, newFloat(Double, 3.14));
Object_AddToRoot (OBJECT(methodCall(list, iterator)));
} }
Class *Object_class; Class *Object_class;
@ -206,7 +229,7 @@ Object_Init (void)
classInit(List); classInit(List);
classInit(ArrayList); classInit(ArrayList);
rootObj = new(ArrayList, classObj(Object), NULL);
/* Run test */ /* Run test */
Object_Test(); Object_Test();
@ -215,7 +238,18 @@ Object_Init (void)
void void
Object_Garbage_Collect (void) Object_Garbage_Collect (void)
{ {
unsigned int amount; static unsigned int frames = 0;
if ((amount = Garbage_Amount()))
Garbage_Collect (amount / 2 + 1); frames++;
if (frames % 2000 == 0) {
Sys_DPrintf("GC: Marking...\n");
Garbage_Do_Mark (OBJECT(rootObj));
Sys_DPrintf("GC: Sweeping...\n");
Garbage_Do_Sweep (&allObjs);
}
if (frames % 50 == 0 && Garbage_Pending()) {
Sys_DPrintf("GC: Disposing...\n");
Garbage_Dispose (Garbage_Pending()/2 + 1);
}
} }

View file

@ -1907,13 +1907,13 @@ SV_GarbageCollect (void)
if (pr_gc_count >= pr_gc_interval->int_val) { if (pr_gc_count >= pr_gc_interval->int_val) {
pr_gc_count = 0; pr_gc_count = 0;
PR_GarbageCollect (&sv_pr_state); PR_GarbageCollect (&sv_pr_state);
Object_Garbage_Collect ();
} }
} else { } else {
// Make sure the count gets reset if the gc is disabled. I // Make sure the count gets reset if the gc is disabled. I
// could use a callback, but I'm lazy // could use a callback, but I'm lazy
pr_gc_count = 0; pr_gc_count = 0;
} }
Object_Garbage_Collect ();
} }
void void