/*
* @(#)gc.c 1.94 95/12/06
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
/*
* Java memory management and garbage collection
*
* Implementation notes:
*
* We really don't need to allocate mark bits for soft references.
* It might be better to keep a small list of the soft references we
* find while doing the mark phase. This would save 24KB of mark bits,
* and shouldn't cost too much in the way of cpu.
*
* I [Tim] tried to split this file up into two: heap.c, covering heap
* initialization and allocation routines, and gc.c, covering GC and heap
* expansion. Doing so changed the basic heap pointers from statics,
* and the result (for the SunPro compiler, at least), was a 10% slowdown
* of the garbage collector. So now we've got a big file again.
*/
#include "typedefs.h"
#include "profile.h"
#include "tree.h"
#include "oobj.h"
#include "interpreter.h"
#include "typecodes.h"
#include "monitor.h"
#include "javaThreads.h"
#include "signature.h"
#include "monitor_cache.h"
#include "finalize.h"
#include "sys_api.h"
#include "java_lang_Thread.h"
int manageAllocFailure(long n, int overflow_type, int overflow_act);
extern void runFinalization(void);
/* Define this if you want lots of verbose info at runtime */
#define TRACEGC 1
int tracegc = 0;
/*
* Lock against heap modification.
*/
sys_mon_t *_heap_lock;
#define HEAP_LOCK_INIT() monitorRegister(_heap_lock, "Heap lock")
#define HEAP_LOCK() sysMonitorEnter(_heap_lock)
#define HEAP_UNLOCK() sysMonitorExit(_heap_lock)
#define HEAP_LOCKED() sysMonitorEntered(_heap_lock)
extern sys_mon_t *_hasfinalq_lock;
/*
* Define this if you want the mark phase to detect pointers into the
* interior of objects.
*/
/* #define CHECK_INTERIOR_POINTERS */
#define OBJECTGRAIN 8
#define HANDLEGRAIN 8
#define BITSPERCHAR 8
/*
* Heap layout, pointers and pointer aliases set by SetLimits():
*
* |----------------------------mapped memory---------------------------|
* |------------------committed memory----------------|
*
* -------------------------------------------------------------------
* | ... <| handles | objects |> ... |
* -------------------------------------------------------------------
* ^ ^ ^ ^ ^ ^
* | | | | | |
* | hpool | hpoollimit opoollimit |
* heapbase | | opool | heaptop
* hpmin hpmax opmin opmax
*
* Constraints:
* hpool/hpmin and hpoollimit are HANDLEGRAIN aligned
* opool/opmin must be OBJECTGRAIN aligned *plus* sizeof(hdr)
* the committed memory must be contained by the mapped memory
*/
/* The extent of the object pool (must be committed memory) */
static unsigned char *opool, *opoollimit, *opoolhand;
/* The extent of the handle pool (must be committed memory) */
static unsigned char *hpool, *hpoollimit, *hpoolhand;
/* The extent of the mapped memory */
static unsigned char *heapbase, *heaptop;
#define ValidObject(p) ((((int)(p)) & (OBJECTGRAIN-1)) == 0 && \
(unsigned char *)(p) >= opmin && \
(unsigned char *)(p) < opmax)
#define ValidHandle(p) (((int) (p) & (sizeof(JHandle)-1)) == 0 && \
(unsigned char *)(p) >= hpmin && \
(unsigned char *)(p) <= hpmax)
/* ValidHorO() assumes OBJECTGRAIN=sizeof(JHandle)... */
#define ValidHorO(p) (((int) (p) & (OBJECTGRAIN-1)) == 0 && \
(unsigned char *)(p) >= hpmin && \
(unsigned char *)(p) <= opmax)
#define SetLimits() \
register unsigned char *const opmin = opool, \
*const opmax = opoollimit, \
*const hpmin = hpool, \
*const hpmax = hpoollimit-sizeof(JHandle)
static unsigned int *markbits;
#define BITSPERMARK (BITSPERCHAR * sizeof(*markbits)) /* 32 */
static long FreeObjectCtr;
static long TotalObjectCtr;
static long FreeHandleCtr;
static long TotalHandleCtr;
static long marksize;
static long markmax;
static long FreeMemoryLowWaterMark;
unsigned char *gc0(int, unsigned int);
/*
* Memory block header (bottom three bits are flags):
*
* -------------------------------------------------------------
* | <--- length --->| unused | <- obj swapped -> | <- free -> |
* -------------------------------------------------------------
* 31 3 2 1 0
*/
typedef long hdr;
#define obj_geth(p) (*((hdr *)(p)))
#define obj_seth(p, h) (*((hdr *)(p)) = (h))
#define h_len(h) ((h) & ~(OBJECTGRAIN-1))
#define h_free(h) ((h) & 1)
#define h_bumplen(h, l) ((h) += (l))
#define obj_len(p) (obj_geth(p)&~(OBJECTGRAIN-1))
#define obj_setlf(p, l, f) (obj_geth(p) = (l)|(f))
#define obj_bumplen(p, l) (obj_geth(p) += (l))
#define obj_free(p) (obj_geth(p)&1)
#define obj_setfree(p) (obj_geth(p) |= 1)
#define obj_clearfree(p) (obj_geth(p) &= ~1)
#define HardMark 3
#define SoftMark 1
/* Mark bit access assumes contiguity of handles and objects */
#define MARKINDEX(p) (((unsigned char *)(p) - hpmin) >> 7)
#define BITOFFSET(p) ((((unsigned char *)(p) - hpmin) >> 2) & 0x1e)
#define _MarkPtr(p, v) (markbits[MARKINDEX(p)] |= (v) << BITOFFSET(p))
#define _ClearMarkPtr(p, v) (markbits[MARKINDEX(p)] &= ~((v) << BITOFFSET(p)))
#define _IsMarked(p) ((markbits[MARKINDEX(p)] >> BITOFFSET(p)) &3)
#define MarkPtr(p, v) _MarkPtr(((unsigned int) (p) & ~(OBJECTGRAIN - 1)), v)
#define ClearMarkPtr(p, v) _ClearMarkPtr(((unsigned int)(p)&~(OBJECTGRAIN-1)),v)
#define IsMarked(p) _IsMarked((unsigned int) (p) & ~(OBJECTGRAIN - 1))
#define SOFTREFBAGSIZE 200 /* max number of soft refs to kill in one cycle */
/*
* Types of overflows: we might respond to an overflow of a particular
* error differently, e.g. expanding only the overflowing area.
*/
#define OVERFLOW_NONE 0
#define OVERFLOW_OBJECTS 1
#define OVERFLOW_HANDLES 2
/*
* Possible actions to take on overflows. manageAllocFailure()
* decides between these.
*/
#define OVERFLOW_ACT_FAIL 0
#define OVERFLOW_ACT_GC 1
#define OVERFLOW_ACT_FINALIZE 2
#define OVERFLOW_ACT_REFS 3
#define OVERFLOW_ACT_EXPAND 4
#define OVERFLOW_ACT_DESPERATE 5
/*
* Rough counter of heap modification events: a lack of known permu-
* tations of the heap is used to suppress async GC. While this isn't
* a perfect indicator of good times to GC, it is unlikely that there
* will be many good times to GC when we aren't turning over memory.
* Current incrementers of heap_memory_changes include realObjectAlloc()
* and execute_finalizer().
*/
int heap_memory_changes = 0;
int64_t
TotalObjectMemory(void)
{
return int2ll(TotalObjectCtr);
}
int64_t
FreeObjectMemory(void)
{
return int2ll(FreeObjectCtr);
}
int64_t
TotalHandleMemory(void)
{
return int2ll(TotalHandleCtr);
}
int64_t
FreeHandleMemory(void)
{
return int2ll(FreeHandleCtr);
}
static HObject *
realObjAlloc(struct methodtable *mptr, long n0)
{
register unsigned char *p;
register unsigned char *limit;
register long n;
HObject *ret;
int overflow_type = OVERFLOW_NONE;
int on_overflow = OVERFLOW_ACT_GC;
n = (n0 + sizeof(hdr) + (OBJECTGRAIN - 1)) & ~(OBJECTGRAIN - 1);
HEAP_LOCK();
p = opoolhand;
limit = opoollimit;
while (1) {
while (p < limit) { /* search for a free block */
register unsigned char *next;
register hdr h = obj_geth(p);
register hdr h2;
sysAssert(h_len(h) >= sizeof(hdr));
next = p + h_len(h);
sysAssert(next <= opoollimit + sizeof(ClassObject));
if (h_free(h)) {
while (h_free(h2 = obj_geth(next))) {
h_bumplen(h, h_len(h2));
next = p + h_len(h);
obj_seth(p, h);
}
h2 = h_len(h) - n;
if (h2 >= 0) { /* bingo! */
if (h2 > 0) {
obj_setlf(p + n, h2, 1);
#ifdef DEBUG
((long *) (p + n))[1] = 0x55555555;
#endif /* DEBUG */
}
obj_setlf(p, n, 0);
#ifdef DEBUG
((long *) p)[1] = 0x55555555;
#endif /* DEBUG */
opoolhand = p + n;
sysAssert(((int) p + sizeof(hdr) & (OBJECTGRAIN - 1)) == 0);
FreeObjectCtr -= n;
sysAssert(FreeObjectCtr >= 0);
goto GotIt;
}
}
p = next;
}
if (limit == opoollimit && opoolhand > opool) {
/* not found, search other half */
p = opool;
limit = opoolhand;
if (opoolhand == opoollimit) {
opoolhand = p;
}
continue;
}
#ifdef DEBUG
if (verbosegc) {
fprintf(stderr, "<GC: out of object space wanting %d bytes>\n", n);
}
#endif /* DEBUG */
overflow_type = OVERFLOW_OBJECTS;
Overflow:
/* Allocation failed for lack of space: garbage collect or expand */
on_overflow = manageAllocFailure(n, overflow_type, on_overflow);
if (on_overflow == OVERFLOW_ACT_FAIL) {
/* Tried everything, couldn't satisfy request, so fail */
HEAP_UNLOCK();
return 0;
} else {
/* opoolhand has been reset by GC */
p = opool;
limit = opoollimit;
}
}
GotIt: /* p points to new object */
ret = AllocHandle(mptr, (ClassObject *) (p + sizeof(hdr)));
if (ret == 0) {
obj_setfree(p);
FreeObjectCtr += obj_len(p);
sysAssert(FreeObjectCtr <= TotalObjectCtr);
/* on_overflow is OVERFLOW_ACT_GC */
overflow_type = OVERFLOW_HANDLES;
goto Overflow;
}
heap_memory_changes++;
HEAP_UNLOCK();
return ret;
}
/*
* REMIND: Why don't we keep free handles in a linked list???
*/
HObject *
AllocHandle(struct methodtable *mptr, ClassObject *p)
{
register unsigned char *hp = hpoolhand;
register unsigned char *limit = hpoollimit - sizeof(JHandle);
if (p == 0) {
return 0;
}
while (hp <= limit) {
if (((JHandle *) hp)->obj == 0) {
goto GotHandle;
} else {
hp += sizeof(JHandle);
}
}
limit = hpoolhand;
hp = hpool;
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, "<GC: handle pool wraps>\n");
}
#endif /* TRACEGC */
while (hp < limit) {
if (((JHandle *) hp)->obj == 0) {
goto GotHandle;
} else {
hp += sizeof(JHandle);
}
}
#ifdef DEBUG
if (verbosegc) {
fprintf(stderr, "<GC: out of handle space>\n");
}
#endif /* DEBUG */
return 0;
GotHandle:
((JHandle *) hp)->methods = mptr;
((JHandle *) hp)->obj = (ClassObject *) p;
hpoolhand = hp + sizeof(JHandle);
FreeHandleCtr -= sizeof(JHandle);
sysAssert(FreeHandleCtr >= 0);
return (HObject *) hp;
}
/*
* Calculate the size in bytes of an array of type t and length l.
*/
int32_t
sizearray(int32_t t, int32_t l)
{
int size = 0;
switch(t){
case T_CLASS:
size = sizeof(OBJECT);
break;
default:
size = T_ELEMENT_SIZE(t);
break;
}
size *= l;
return size;
}
/*
* Allocate an array of type 't' with initial size 'l'. This routine
* takes care of setting the method table pointer correctly. It also
* takes into consideration any special sizeing requirements, like
* those needed for arrays of classes.
*/
HObject *
ArrayAlloc(int t, int l)
{
sysAssert(t >= T_CLASS && t < T_MAXNUMERIC);
/*
* Uncomment me to find all the places where the code creates zero length
* arrays.
*
* if (l == 0) {
* extern void DumpThreads();
* printf("zero length array created %s[%d]\n", arrayinfo[t].name, l);
* DumpThreads();
* }
*/
/*
* Check whether there is in principle enough memory to satisfy the
* allocation. We want to fail this early because the array size
* calculation might otherwise overflow, which would pass a garbage
* size to realObjAlloc(). Note that a T_CLASS array allocates
* an extra OBJECT; hence the l - 1 which otherwise makes the test
* one element conservative. Note that l may be 0!
*/
if (l && ((l - 1) > ((heaptop - heapbase) /
(t == T_CLASS ? sizeof(OBJECT) : T_ELEMENT_SIZE(t))))) {
return 0;
}
return realObjAlloc((struct methodtable *) mkatype(t, l),
sizearray(t, l) + (t == T_CLASS ? sizeof(OBJECT) : 0));
}
/*
* Allocate an object. This routine is in flux. For now, the second
* parameter should always be zero and the first must alwaysd point to
* a valid classblock. This routine should not be used to allocate
* arrays; use ArrayAlloc for that.
*/
HObject *
ObjAlloc(ClassClass *cb, long n0)
{
HObject *handle;
#ifdef DEBUG
if (n0 != 0 || cb == 0) {
sysAbort();
}
#endif /* DEBUG */
n0 = cbInstanceSize(cb);
handle = realObjAlloc(cbMethodTable(cb), n0);
/*
* If the class is a normal class and has a finalization method, flag
* the instance and prepend it to the head of the HasFinalizerQ. Note
* that handle can be nil if we are out of memory!
*/
if (handle && obj_flags(handle) == T_NORMAL_OBJECT && cb->finalizer) {
finalizer_t *final;
final = (finalizer_t *) sysMalloc(sizeof(finalizer_t));
final->handle = handle;
HASFINALQ_LOCK();
final->next = HasFinalizerQ;
HasFinalizerQ = final;
HASFINALQ_UNLOCK();
}
return handle;
}
/*
* Search the handle table for instances of class Ref, and clear the
* object pointer in the lowest priority group. If async_call is true,
* we are being called asynchronously, and should watch for pending
* interrupts.
*
* Return the number of refs zeroed, and set the int argument pointer
* to the number of refs found in the system.
*/
static int
clearRefPointers(int async_call, int free_space_goal, int *totalrefs)
{
int nsoftrefs = 0;
struct {
JHandle *ref;
long priority;
} bag[SOFTREFBAGSIZE];
JHandle *hp;
int ninbag = 0;
int freed = 0;
SetLimits();
for (hp = (JHandle *) hpool; hp <= (JHandle *) hpmax; hp++) {
if (ValidObject(hp->obj)
&& obj_flags(hp) == T_NORMAL_OBJECT
&& CCIs(obj_classblock(hp), SoftRef)
&& ValidHandle(obj_getslot(hp, 0))
&& IsMarked(obj_getslot(hp, 0)) == SoftMark) {
int prio = obj_getslot(hp, 1);
if (ninbag >= SOFTREFBAGSIZE
&& prio < bag[SOFTREFBAGSIZE - 1].priority) {
ninbag--;
}
if (ninbag < SOFTREFBAGSIZE) {
int i = ninbag;
while (i > 0 && prio < bag[i - 1].priority) {
bag[i] = bag[i - 1];
i--;
}
bag[i].ref = hp;
bag[i].priority = prio;
ninbag++;
}
nsoftrefs++;
}
}
if (async_call && INTERRUPTS_PENDING()) {
return 0;
}
if (ninbag) {
int i;
int freemem;
/* Don't free more than half the Refs when there are more than 10 */
if ((i = (nsoftrefs + 1) >> 1) <= ninbag && ninbag > 10) {
ninbag = i;
}
freemem = FreeObjectCtr;
for (i = ninbag; --i >= 0;) {
/*
* Only free as many refs as needed to meet free_space_goal.
* Of course, this assumes that there are no non-ref references
* to the freed objects, which needs not be the case.A
*/
freemem += obj_len((char *)bag[i].ref - sizeof(hdr));
#ifdef TRACEGC
if (tracegc > 1) {
fprintf(stderr, "clear soft ref: 0x%x: %s\n",
(unsigned int) bag[i].ref, Object2CString(bag[i].ref));
}
#endif /* TRACEGC */
obj_setslot(bag[i].ref, 0, 0);
freed++;
/* This is a weak check as it doesn't require contiguous memory */
if (freemem >= free_space_goal) {
break;
}
}
}
*totalrefs = nsoftrefs;
return freed; /* Return the number of refs freed */
}
/*
* Expand the mark bits array. We don't care whether the expansion that
* led to this is of the handle or object space, just that the contiguous
* memory that the mark bits must span has expanded by the given amount.
*
* There is no slack in expanding the mark bits: we have already alloca-
* ted the additional memory that the increment represents, so we have
* got to be able to allocate mark bits to cover it. We don't need to
* initialize the new mark bits: they're zeroed on the start of each GC.
*
* Note: Given that sysCommitMem() should be assumed to actually allocate
* swap space, it is arguable that we should put off committing memory
* for the mark bits until GC actually needs it. But GC is pretty much
* inevitable in Java, so it's not clear there's any real benefit.
*/
int
expandMarkBits(long incr)
{
long markincr;
unsigned int *incrbits;
markincr = ((incr/(OBJECTGRAIN*BITSPERMARK) + 1) * 2) * sizeof(*markbits);
if (marksize + markincr > markmax) {
if (verbosegc) {
fprintf(stderr, "<GC: tried to expand mark bits over limit>\n");
}
return 0;
} else {
incrbits = sysCommitMem((char *)markbits+marksize, markincr, &markincr);
if (!incrbits) {
if (verbosegc) {
fprintf(stderr, "<GC: expansion of mark bits failed>\n");
}
return 0;
}
sysAssert(incrbits == (unsigned int *)((char *)markbits + marksize));
marksize += markincr;
return 1;
}
}
/*
* Expand the handle space. The behavior of the handle space is simpler
* than that of the object space: handles never move and the space can't
* be fragmented, but that also means that contracting the handle space
* is easy to block.
*
* If we were quicker to allocate new handles than GC to reclaim handles,
* initially expanding by a fixed amount would be quick and easy. But
* given the attempt to stay small as much as possible, we are instead
* using a strategy similar to the object store's, where we GC first,
* and if we have to expand, we preallocate new space that scales with
* the amount of existing space, subject to a minimum.
*
* Be sure to zero all the new handles: a free handle is recognized by
* having its obj field zeroed. Remember that the base of the handle
* space must be HANDLEGRAIN aligned within the committed memory.
*
* If we are going to seriously try to contract the handle space we
* should instead allocate handles from higher addresses toward lower
* addresses.
*/
#undef max
#define max(a, b) ((a) < (b) ? (b) : (a))
#define MIN_HANDLE_EXPANSION 4*1024
float preallocFactor = 0.25; /* REMIND: NEEDS TO MOVE! */
int
expandHandleSpace(void)
{
long incr;
/*
* Expand by the maximum of a constant and the amount we need to
* make FreeObjectCtr-n >= TotalObjectCtr*preallocFactor.
*/
incr = MIN_HANDLE_EXPANSION;
incr = max(incr, TotalHandleCtr*preallocFactor - FreeHandleCtr);
hpool = hpool - incr;
if (hpool < heapbase) { /* We might get by with less... */
if (verbosegc) {
fprintf(stderr, "<GC: tried to expand handle space over limit>\n");
}
return 0;
} else {
hpoolhand = hpool;
hpool = sysCommitMem(hpool, incr, &incr);
if (!hpool) {
if (verbosegc) {
fprintf(stderr, "<GC: expansion of handle space failed>\n");
}
return 0;
}
/* Need to be HANDLEGRAIN aligned within committed memory */
hpool = (unsigned char *)
(((int) hpool + HANDLEGRAIN-1) & ~(HANDLEGRAIN-1));
memset(hpool, 0, hpoolhand - hpool);
hpoolhand = hpool;
FreeHandleCtr += incr;
TotalHandleCtr += incr;
if (verbosegc) {
fprintf(stderr,
"<GC: expanded handle space by %d to %d bytes, %d%% free>\n",
incr, TotalHandleCtr, 100 * FreeHandleCtr / TotalHandleCtr);
}
return expandMarkBits(incr);
}
}
/*
* Expand the object space. To go on we need to at least cover the
* currently failing allocation, meaning we need a contiguous chunk of
* its size minus the amount of space in the trailing free chunk, if any.
* However, we normally also want to leave a user-controllable amount
* of preallocated space free *after* we have satisfied the current
* allocation. This preallocated space is calculated as a percentage
* of the total free space, so given possible fragmentation, that free
* space does not guarantee large contiguous allocations. But given
* that most allocations are not that large, preallocation can generally
* reduce the number of interruptions due to memory management operations.
* The user controls the rate of preallocation by varying the value of
* the preallocFactor variable.
*
* [REMIND: Currently preallocFactor is global. That is probably right
* for a single application, but could it lead to applets fighting over
* its value???]
*
* The last_free parameter passed in here is the last free chunk
* of memory as determined by the most recent heap compaction in
* *this* round of memory management. If the last chunk isn't known,
* last_free is 0. There is no guarantee that the last chunk abuts
* the end of the object space, but if it does, any new memory allocated
* here is coalesced with the last chunk.
*
* Because we are expanding at the top of the space, we leave opoolhand
* where it is.
*/
int
expandObjectSpace(long n, unsigned char *last_free)
{
long incr = 0;
unsigned char *incrptr;
/*
* Expand by the maximum of the minimum we need to satisfy the current
* allocation (taking into account the last_free chunk) and the amount
* we need such that FreeObjectCtr-n >= TotalObjectCtr*preallocFactor.
*/
if (last_free) {
if (n > obj_len(last_free)) {
/* May be expanding to satisfy an allocation, not preallocate */
incr = n - obj_len(last_free);
} else {
/* We must be expanding to preallocate */
incr = 0;
}
} else {
incr = n;
}
incr = max(incr, TotalObjectCtr*preallocFactor - (FreeObjectCtr-n));
/* Always scale incr up to a multiple of OBJECTGRAIN */
incr = (incr + OBJECTGRAIN - 1) & ~(OBJECTGRAIN - 1);
if (opoollimit + incr > heaptop) {
if (verbosegc) {
fprintf(stderr, "<GC: tried to expand object space over limit>\n");
}
return 0;
} else {
/* We could go on with anything down to n - obj_len(last_free) */
incrptr = sysCommitMem(opoollimit + sizeof(hdr), incr, &incr);
if (!incrptr) {
if (verbosegc) {
fprintf(stderr, "<GC: expansion of object space failed>\n");
}
return 0;
}
sysAssert(incrptr == opoollimit + sizeof(hdr));
if (last_free && last_free + obj_len(last_free) == opoollimit) {
/*
* The last free chunk abuts the end of the object space, so
* we can coalesce it with the new memory.
*/
obj_bumplen(last_free, incr);
#ifdef DEBUG
((long *) last_free)[1] = 0x55555555;
#endif /* DEBUG */
} else {
/*
* The last free chunk either doesn't abut the end of the
* object space or we don't know where it is, so we don't
* try to coalesce the new space.
*/
obj_setlf(opoollimit, incr, 1);
#ifdef DEBUG
((long *) opoollimit)[1] = 0x55555555;
#endif /* DEBUG */
}
opoollimit = opoollimit + incr;
obj_setlf(opoollimit, 0, 0);
FreeObjectCtr += incr;
TotalObjectCtr += incr;
if (verbosegc) {
fprintf(stderr,
"<GC: expanded object space by %d to %d bytes, %d%% free>\n",
incr, TotalObjectCtr, 100 * FreeObjectCtr / TotalObjectCtr);
}
return expandMarkBits(incr);
}
}
/*
* Deal with an out-of-memory situation caused by an overflow within
* the Java heap, either of the handle area or the object area. This
* thing is called with the heap lock, so doesn't have to worry about
* someone else coming along and allocating or causing another GC.
*/
int
manageAllocFailure(long n, int overflow_type, int overflow_act) {
int freeObject;
int freeHandle;
int nsoftrefs = 0;
int ninbag = 0;
unsigned char *last_free = 0;
sysAssert(HEAP_LOCKED());
/*
* As a special case, if the preallocFactor >= 1.0, then there is
* no way that GC will ever satisfy the preallocation criterion.
* Take this as a signal to turn off GC entirely and continue by
* expansion alone. REMIND: Should we have a distinct switch to
* turn off GC?
*/
if (preallocFactor >= 1.0) {
overflow_act = OVERFLOW_ACT_EXPAND;
}
while (1) {
/*
* Because of fragmentation, it isn't good enough only to know
* how much free space is available. Before we go try to allocate
* again, we want to know whether the last memory management
* operation we did made new progress. So each time around the
* while() we reset the progress indicators.
*/
if (overflow_type == OVERFLOW_OBJECTS) {
freeObject = FreeObjectCtr;
} else {
freeHandle = FreeHandleCtr;
}
switch (overflow_act) {
case OVERFLOW_ACT_GC:
/*
* Try normal garbage collection; otherwise try finalizing.
*/
last_free = gc0(0, n);
/*
* I'm not sure it's really worth finalizing and freeing
* refs for a handle overflow, as those are not good sources
* of many handles. The alternative is going straight to
* OVERFLOW_ACT_EXPAND if overflow_type == OVERFLOW_HANDLES.
*/
overflow_act = OVERFLOW_ACT_FINALIZE;
break;
case OVERFLOW_ACT_FINALIZE:
/*
* Try finalizing then GCing again; next free refs
*
* If there are objects on the FinalizeMeQ they are known to
* be garbage that can't be freed until they are finalized.
* Synchronously finalize them, then garbage collect again.
*/
overflow_act = OVERFLOW_ACT_REFS;
if (FinalizeMeQ) {
if (verbosegc) {
int count = 0;
finalizer_t *finalizer = FinalizeMeQ;
while (finalizer) {
count++;
finalizer = finalizer->next;
}
fprintf(stderr,
"<GC: synchronously running %d finalizers>\n",
count);
}
runFinalization();
last_free = gc0(0, n);
break;
} else {
continue; /* Don't bother checking for progress */
}
case OVERFLOW_ACT_REFS:
/*
* If there are any refs defined, free some and GC again;
* keep trying this so long as there remain refs to free,
* and otherwise expand.
*/
if (ninbag = clearRefPointers(0, TotalObjectCtr, &nsoftrefs)) {
if (verbosegc) {
fprintf(stderr, "<GC: zeroed %d of %d soft refs>\n",
ninbag, nsoftrefs);
}
last_free = gc0(0, n);
overflow_act = OVERFLOW_ACT_REFS;
break;
} else {
overflow_act = OVERFLOW_ACT_EXPAND;
continue; /* Don't bother checking for progress */
}
case OVERFLOW_ACT_EXPAND:
/*
* Expand the overflowing area: Successful expansion should
* guarantee that the allocation will succeed, and should not
* over(pre)allocate.
*/
overflow_act = OVERFLOW_ACT_DESPERATE;
if (overflow_type == OVERFLOW_HANDLES) {
if (expandHandleSpace()) {
/* We got *some* memory */
return overflow_act;
}
} else if (overflow_type == OVERFLOW_OBJECTS) {
if (expandObjectSpace(n, last_free)) {
/* We got *some* memory */
return overflow_act;
}
}
break;
case OVERFLOW_ACT_DESPERATE:
if (verbosegc) {
fprintf(stderr, "<GC: totally out of heap space>\n");
}
/* Failed to make any progress: really out of heap */
return OVERFLOW_ACT_FAIL;
default:
return OVERFLOW_ACT_FAIL;
}
/*
* Test whether the last memory management operation made
* sufficient progress to try allocating again. Otherwise,
* go directly to trying the next operation. Subtlety: because
* of fragmentation, apparently adequate free space is not always
* enough, so we also require evidence of recent progress. Even
* so, there's still no guarantee allocation will go through.
* Note that we subtract off the current allocation amount: the
* preallocated memory is the amount after this allocation.
*/
if (overflow_type == OVERFLOW_OBJECTS) {
if (FreeObjectCtr > freeObject &&
FreeObjectCtr-n >= TotalObjectCtr*preallocFactor) {
return overflow_act; /* Try to allocate again */
} /* else go back and switch on new overflow_act */
} else {
if (FreeHandleCtr > freeHandle &&
FreeHandleCtr-sizeof(JHandle) >= TotalHandleCtr*preallocFactor) {
return overflow_act; /* Try to allocate again */
} /* else go back and switch on new overflow_act */
}
}
}
void
markChildren(register JHandle * h, register JHandle * limit)
{
register ClassClass *cb;
register JHandle *sub, *needsmark;
register ClassObject *p;
SetLimits();
ScanNext:
needsmark = 0;
p = unhand(h);
#ifdef TRACEGC
if (tracegc > 1) {
fprintf(stderr, "<%X/%X>\n", h, p);
fprintf(stderr, " < %s\n", Object2CString(h));
}
#endif /* TRACEGC */
switch (obj_flags(h)) {
case T_NORMAL_OBJECT:
cb = obj_classblock(h);
do {
register long n = cb->fields_count;
register struct fieldblock *fb = cbFields(cb);
while (--n >= 0) {
if ((fieldIsArray(fb) || fieldIsClass(fb))
&& !(fb->access & ACC_STATIC)) {
sub = *(JHandle **) ((char *) p + fb->u.offset);
if (ValidHandle(sub) && !IsMarked(sub)) {
MarkPtr(sub, CCIs(cb, SoftRef)
&& fb->u.offset == 0
? SoftMark : HardMark);
#ifdef TRACEGC
if (tracegc > 1) {
fprintf(stderr, " mo %s\n", Object2CString(sub));
}
#endif /* TRACEGC */
if (sub < limit) {
if (needsmark) {
markChildren(needsmark, limit);
}
needsmark = sub;
}
}
}
fb++;
}
if (cbSuperclass(cb) == 0) {
break;
}
cb = unhand(cbSuperclass(cb));
} while (cb);
break;
case T_CLASS: { /* an array of classes */
register long n = obj_length(h);
while (--n >= 0) {
sub = ((ArrayOfObject *) p)->body[n];
if (ValidHandle(sub) && !IsMarked(sub)) {
MarkPtr(sub, HardMark);
#ifdef TRACEGC
if (tracegc > 1) {
fprintf(stderr, " ma %s\n", Object2CString(sub));
}
#endif /* TRACEGC */
if (sub < limit) {
if (needsmark) {
markChildren(needsmark, limit);
}
needsmark = sub;
}
}
}
}
}
if (h = needsmark) {
goto ScanNext; /* tail recursion */
}
#ifdef TRACEGC
if (tracegc > 1) {
fprintf(stderr, " > %s\n", Object2CString(h));
}
#endif /* TRACEGC */
}
static int
scanThread(sys_thread_t *t, void *arg)
{
extern JHandle *getThreadNext();
JHandle *me = (JHandle *) arg;
JHandle *p = (JHandle *) sysThreadGetBackPtr(t);
unsigned char **ssc, **limit;
stackp_t base;
SetLimits();
#ifdef DEBUG
if (!ValidHandle(p)) {
sysAbort();
}
#endif /* DEBUG */
#ifdef TRACEGC
if (tracegc > 1 && !IsMarked(p)) {
if (((JHandle *)p)->obj && ((JHandle *)p)->methods) {
fprintf(stderr, " ms %s\n", Object2CString((JHandle *)p));
} else {
fprintf(stderr, " ms %8X\n", p);
}
}
#endif /* TRACEGC */
/* Mark thread object */
MarkPtr(p, HardMark);
base = threadStackBase((TID)p);
ssc = threadStackPointer((TID)p);
if (p == me) {
ssc = (unsigned char **) &me;
}
if (ssc == 0 || base == 0 || (ssc == base)) {
/*
* If the stack does not have a top of stack pointer or a base
* pointer then it hasn't run yet and we don't need to scan
* its stack. When exactly each of these data becomes available
* may be system-dependent, but we need both to bother scanning.
*/
return SYS_OK;
}
limit = (unsigned char **) base;
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, "%8X [%10X,%10X] %d\n", p, ssc, limit, limit - ssc);
}
#endif /* TRACEGC */
sysAssert(ssc != limit);
while (ssc < limit) {
register unsigned char *ptr = *ssc;
if (ValidHorO(ptr)) {
#ifdef TRACEGC
if (tracegc > 1 && !IsMarked(ptr) && ValidHandle(ptr)) {
if (((JHandle *)ptr)->obj && ((JHandle *)ptr)->methods) {
fprintf(stderr, " ms %s\n", Object2CString((JHandle *) ptr));
} else {
fprintf(stderr, " ms %8X\n", ptr);
}
}
#endif /* TRACEGC */
MarkPtr(ptr, HardMark);
}
ssc++;
}
{
ExecEnv *ee = (ExecEnv *)THREAD(p)->eetop;
JavaFrame *frame;
register unsigned char *ptr;
if ((ee != 0) && ((frame = ee->current_frame) != 0)) {
stack_item *top_top_stack = frame->current_method
? &frame->ostack[frame->current_method->maxstack]
: frame->optop;
limit = (unsigned char **)top_top_stack;
for(;;) {
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, " scanning frame %8X\n", frame);
}
#endif /* TRACEGC */
for ( ssc= (unsigned char **)(frame->ostack);
ssc < limit; ssc++) {
ptr = *ssc;
if (ValidHorO(ptr)) {
#ifdef TRACEGC
if (tracegc > 1 && !IsMarked(ptr) && ValidHandle(ptr)) {
fprintf(stderr, " ms %8X\n", ptr);
}
#endif /* TRACEGC */
MarkPtr(ptr, HardMark);
}
}
ssc = (unsigned char **)(frame->vars);
if (ssc) {
limit = (unsigned char **)frame;
for (; ssc < limit; ssc ++) {
ptr = *ssc;
if (ValidHorO(ptr)) {
#ifdef TRACEGC
if (tracegc > 1 && !IsMarked(ptr) && ValidHandle(ptr)) {
fprintf(stderr, " ms %8X\n", ptr);
}
#endif /* TRACEGC */
MarkPtr(ptr, HardMark);
}
}
}
frame = frame->prev;
if (!frame) break;
limit = (unsigned char **)(frame->optop);
}
} else {
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, "ee=%x\n", ee);
if (ee) {
fprintf(stderr, "frame=%x\n", ee->current_frame);
}
}
#endif /* TRACEGC */
}
}
return SYS_OK;
}
static void
scanThreads(void)
{
JHandle *self;
int err;
self = (JHandle *) sysThreadGetBackPtr(sysThreadSelf());
err = sysThreadEnumerateOver(scanThread, (void *) self);
/* REMIND - should do something with the return value. */
}
/* Look through the constant pool of each of the classes to see if it
* contains any objects (like Strings) that need to be marked.
*/
static void
scanClasses(void)
{
int i, j;
ClassClass **pcb = binclasses;
struct fieldblock *fb;
SetLimits();
for (j = nbinclasses; --j >= 0; pcb++) {
ClassClass *cb = *pcb;
if (!cb) continue;
if (CCIs(cb, Resolved)) {
union cp_item_type *constant_pool = cbConstantPool(cb);
union cp_item_type *cpp = constant_pool+ CONSTANT_POOL_UNUSED_INDEX;
union cp_item_type *end_cpp = &constant_pool[cb->constantpool_count];
for ( ; cpp < end_cpp; cpp++) {
void *ptr = (*cpp).p;
if (ValidHorO(ptr)) {
#ifdef TRACEGC
if (tracegc > 1 && !IsMarked(ptr) && ValidHandle(ptr)) {
if (((JHandle *)ptr)->obj && ((JHandle *)ptr)->methods) {
fprintf(stderr, " ms %s\n",
Object2CString((JHandle *) ptr));
} else {
fprintf(stderr, " ms %8X\n", ptr);
}
}
#endif /* TRACEGC */
MarkPtr(ptr, HardMark);
}
}
}
/* Scan class definitions looking for statics */
for (i = cb->fields_count, fb = cbFields(cb); --i >= 0; fb++) {
if ((fieldIsArray(fb) || fieldIsClass(fb)) && (fb->access & ACC_STATIC)) {
JHandle *sub = *(JHandle **)normal_static_address(fb);
if (ValidHandle(sub) && !IsMarked(sub)) {
MarkPtr(sub, HardMark);
#ifdef TRACEGC
if (tracegc > 1) {
fprintf(stderr, " mc %s\n", Object2CString(sub));
}
#endif /* TRACEGC */
}
}
}
/* don't have to mark cb->superclass, because it's caught by
the HandleToSelf for that class */
if (ValidHandle(cbHandle(cb))) {
MarkPtr((JHandle *) cbHandle(cb), HardMark);
}
if (ValidHandle(cbClassnameArray(cb))) {
MarkPtr((JHandle *) cbClassnameArray(cb), HardMark);
}
if (ValidHandle(cbLoader(cb))) {
MarkPtr((JHandle *) cbLoader(cb), HardMark);
}
}
}
/*
* Scan the heap and chase pointers. Scanning from pointers into the
* middle of objects is normally disabled, apparently because it was
* slow. This should be revisited, especially if it reduces pinning.
*/
static void
scanHeap(void)
{
SetLimits();
unsigned char *o, *oo, *next;
int i, size;
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, "\nRecursive mark scan:\n");
}
#endif /* TRACEGC */
#ifdef CHECK_INTERIOR_POINTERS
/*
* Search the entire memory pool looking for references into the
* the middle of objects. If any are found, mark the object they
* are part of. Pointers into the middle of objects can happen
* frequently, especially in the case of arrays.
*
* This piece of code is tricky because it needs to be fast.
* It is basically walking through most of the mark bits looking
* for set bits. It does it in 32-bit increments for speed.
*/
o = oo = opmin;
next = o + obj_len(o);
size = ((int) (opoollimit - opmin) / (OBJECTGRAIN * BITSPERMARK) + 1) * 2;
for (i=0; i < size; i++) {
sysAssert(((unsigned int)(oo - opmin) >> 7) == i);
if (markbits[i]) {
unsigned int bits = markbits[i];
unsigned int shifts = OBJECTGRAIN * BITSPERMARK / 2;
while (bits && oo < opmax) {
if (bits & 0x3) {
/* Mark the object, not the header */
MarkPtr((o + sizeof(hdr)), HardMark);
}
bits >>= 2;
shifts -= OBJECTGRAIN;
oo += OBJECTGRAIN;
if (oo == next) {
o = next;
next = o + obj_len(o);
}
}
oo += shifts;
} else {
oo += OBJECTGRAIN * BITSPERMARK / 2;
}
if (oo >= opmax) {
break;
}
while (oo >= next) {
o = next;
next = o + obj_len(o);
}
}
#endif /* CHECK_INTERIOR_POINTERS */
}
/*
* Walk the entire handle pool, and do a recursive search for
* references.
*/
static void
scanHandles(void)
{
SetLimits();
JHandle *hp;
hp = (JHandle *) hpool;
while ((unsigned char *) hp <= hpmax) {
if (hp->obj) {
if (ValidObject(hp->obj) && IsMarked(hp->obj)) {
MarkPtr((JHandle *) hp, HardMark);
}
if (IsMarked(hp)) {
markChildren(hp, hp + 1);
}
}
hp++;
}
}
/*
* Take care of finalization
*
* At this point we have marked all reachable objects. Anything
* not marked and having a finalization method will be queued for
* finalization. However, it is not the case that everything
* else that's unmarked can just be freed. An object to be
* finalized expects everything it references to be retained by
* GC, whether or not those things are themselves finalized. So
* we make a pass over the HasFinalizerQ that moves freed objects
* needing finalization to the FinalizeMeQ. We then make a pass
* over the FinalizeMeQ recursively marking everything on it. It
* is important that there is one pass over each queue: doing it
* in a single pass over the HasFinalizerQ causes different
* behavior as finalizable objects referenced by earlier finali-
* zable objects will be marked and not be themselves finalized.
* We could potentially allow interruption between the two passes.
*
* After this pass we are in a consistent state (only objects that
* are unreachable have been put on the FinalizeMeQ), so we can
* allow async GC to be interrupted. Note that this means that
* an object may be finalized earlier than its non-finalized peers
* are freed. Note that the FinalizeMeQ scan also marks objects that
* were on the FinalizeMeQ when we started, which we needed to
* do anyhow. Removing an object from the HasFinalizerQ ensures
* that we will never try to finalize it again.
*/
static void
prepareFinalization(void)
{
if (HasFinalizerQ) {
SetLimits();
JHandle *hp;
finalizer_t *prev = (finalizer_t *) &HasFinalizerQ;
finalizer_t *final;
/* Pass over HasFinalizerQ: */
while (final = prev->next) {
hp = final->handle;
if (hp->obj && !IsMarked(hp) && ValidObject(hp->obj)) {
/* Important: Put in FinalizeMeQ in reverse order */
prev->next = final->next;
final->next = FinalizeMeQ;
FinalizeMeQ = final;
} else { /* still in use, so leave alone */
prev = final;
}
}
/* Trace the FinalizeMeQ: */
final = FinalizeMeQ;
while (final) {
hp = final->handle;
/* Mark the handle itself */
MarkPtr((JHandle *) hp, HardMark);
/* Recursively mark all objects referencable via the handle */
markChildren(hp, (JHandle *) hpmax);
final = final->next;
}
/* Trace the BeingFinalized object (see comments in finalize.c): */
final = BeingFinalized;
if (final) {
hp = final->handle;
MarkPtr((JHandle *) hp, HardMark);
markChildren(hp, (JHandle *) hpmax);
}
}
}
/*
* Scan for remaining unmarked objects and free them. All objects
* that are to be finalized have been marked.
*/
static long nfreed; /* For -verbosegc */
static long bytesfreed; /* For -verbosegc */
static int
freeSweep(unsigned int free_space_goal)
{
SetLimits();
JHandle *hp;
int do_compact_heap = 1;
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, "\nfree scan:\n");
}
#endif /* TRACEGC */
hp = (JHandle *) hpool;
while ((unsigned char *) hp <= hpmax) {
if (hp->obj && !IsMarked(hp) && ValidObject(hp->obj)) {
#ifdef TRACEGC
if (tracegc) {
fprintf(stderr, " fr %s (%8X/%8X)\n",
Object2CString(hp), hp, hp->obj);
}
#endif /* TRACEGC */
#ifdef DEBUG
memset((char *) hp->obj, 0x55,
obj_len((char *) hp->obj - sizeof(hdr)) - sizeof(hdr));
#endif /* DEBUG */
FreeObjectCtr += obj_len((char *) hp->obj - sizeof(hdr));
sysAssert(FreeObjectCtr <= TotalObjectCtr);
if (verbosegc) {
nfreed++;
bytesfreed += obj_len((char *) hp->obj - sizeof(hdr));
}
if (obj_len((char *)hp->obj - sizeof(hdr)) >= free_space_goal) {
do_compact_heap = 0;
}
obj_setfree((char *) hp->obj - sizeof(hdr));
hp->obj = 0;
#ifdef DEBUG
hp->methods = 0; /* paranoia */
#endif /* DEBUG */
FreeHandleCtr += sizeof(JHandle);
sysAssert(FreeHandleCtr <= TotalHandleCtr);
}
hp++;
}
/* Reset allocation hands, so that freed memory gets used */
opoolhand = opmin;
hpoolhand = hpool;
return do_compact_heap;
}
/*
* Concept: Assuming optimal packing, there are only two places that
* holes, or bubbles in the heap can occur. These are immediately before
* each pinned range of memory, and at the tail of the heap (the last
* range is always free).
*
* This algorithm walks the heap from beginning to end, performing
* several operations:
* 1. Coalesce adjacent free blocks.
* 2. Record bubbles in the free_blocks[] array. This is an array
* containing the biggest free blocks found.
* 3. Copy unpinned, allocated blocks down into the lowest free
* block available.
*
* The goal of this scheme is to leave as many large free blocks available
* as is possible.
*/
static int
compactHeap(int async_call, unsigned char **last_freep)
{
SetLimits();
register JHandle *hp;
unsigned char *p, *next, *prev;
#define NUMFREEBLOCKS 16
unsigned char *free_blocks[NUMFREEBLOCKS];
unsigned int plen;
int blocks_moved = 0;
unsigned char *last_free = 0;
p = opmin;
prev = 0;
memset((char *)free_blocks, 0, sizeof(free_blocks));
/*
* Put a pointer to each object's handle in the first word of the object,
* saving the first word of the object in the object pointer of the
* handle. This way we can compact the heap looking only at the heap,
* then go back and fix up the handles. (we need to scan all objects in
* heap order, not handle order to effectively compact everything).
*/
#define obj_swapped(p) (obj_geth(p) & 2)
#define obj_setswapped(p) (obj_geth(p) |= 2)
#define obj_clearswapped(p) (obj_geth(p) &= ~2)
#define handle_swapped(hp) (IsMarked(hp) & 0x1)
#define handle_setswapped(hp) (MarkPtr(hp, 0x1))
#define handle_clearswapped(hp) (ClearMarkPtr(hp, 0x1))
for (hp = (JHandle *) hpool; hp <= (JHandle *) hpmax; hp++) {
if (ValidObject(hp->obj)) {
OBJECT T = *hp->obj;
unsigned char *p = (unsigned char *)(hp->obj) - sizeof(hdr);
*hp->obj = (OBJECT) hp;
hp->obj = (OBJECT *) T;
if (obj_swapped(p)) {
handle_setswapped(hp);
} else {
obj_setswapped(p);
handle_clearswapped(hp);
}
}
}
/*
* This is the reverse of the above swap. It is called once
* on each allocated block and fixes the handle/pointer swap.
*/
#define REVERSE_SWAP(p) { \
JHandle *hp, *nexthp; \
sysAssert(obj_swapped(p)); \
obj_clearswapped(p); \
hp = *(JHandle **) (p + sizeof(hdr)); \
while (handle_swapped(hp)) { \
nexthp = (JHandle *) (hp->obj); \
hp->obj = (OBJECT *) (p + sizeof(hdr)); \
hp = nexthp; \
} \
*(OBJECT *) (p + sizeof(hdr)) = (OBJECT) hp->obj; \
hp->obj = (OBJECT *) (p + sizeof(hdr)); \
handle_setswapped(hp); \
}
/*
* The main loop - look at every object in the pool
*/
while (p < opmax) {
plen = obj_len(p);
next = p + plen;
if (async_call && INTERRUPTS_PENDING()) {
blocks_moved = 0;
/*
* Cycle through the remaining blocks as quickly as possible,
* reversing all of the handle pointers
*/
while (p < opmax) {
if (!obj_free(p)) {
REVERSE_SWAP(p);
}
p += obj_len(p);
}
last_free = 0;
break;
}
if (obj_free(p)) {
unsigned int i, smallest = 0, len = (unsigned) ~0;
/* 1. Coalesce adjacent free blocks */
while (next && obj_free(next)) {
plen += obj_len(next);
next += obj_len(next);
blocks_moved++;
}
obj_setlf(p, plen, 1);
/*
* Record the most recently seen free block. When compaction
* finishes this will be the highest free block. It is very
* likely that it will span to the end of the object space,
* giving us the option of freeing some of it.
*/
last_free = p;
/* 2. Record the fact that p is a free block */
/*
* Search for the smallest sized free range, and if it
* is smaller than the size of p, replace it with p.
* There may be empty slots in the buffer. Use them
* if possible.
*/
for (i = 0; i < NUMFREEBLOCKS; i++) {
sysAssert(free_blocks[i] == 0 || obj_free(free_blocks[i]));
if (free_blocks[i] == 0 || free_blocks[i] == p) {
len = 0;
smallest = i;
break;
} else if (obj_len(free_blocks[i]) < len) {
smallest = i;
len = obj_len(free_blocks[i]);
}
}
if (len < obj_len(p)) {
free_blocks[smallest] = p;
}
prev = p;
} else if (!IsMarked(p + sizeof(hdr))) {
int i, lowest, prev_block = -1;
unsigned int len, lowest_len;
unsigned char *lowest_addr;
/* 3. Copy p down into the lowest available free block */
/*
* Find lowest block that is big enough to hold the block.
* Ignore 'prev' as we will handle it with a simpler case.
*/
lowest = -1;
lowest_len = 0;
lowest_addr = opmax;
for (i = 0; i < NUMFREEBLOCKS; i++) {
if (!free_blocks[i]) {
continue;
}
if (free_blocks[i] == prev) {
prev_block = i;
}
len = obj_len(free_blocks[i]);
if (len > plen && lowest_addr > free_blocks[i]) {
lowest = i;
lowest_addr = free_blocks[i];
lowest_len = len;
}
}
/* did we find a slot? */
if (lowest >= 0 && lowest != prev_block) {
unsigned char *oldp = p;
/*
* If a slot is found, move the block, and mark the old
* slot as free. If the free space was bigger than we needed,
* break off a new chunk.
*/
sysAssert(obj_free(free_blocks[lowest]));
memmove(free_blocks[lowest], p, plen);
blocks_moved++;
obj_setfree(p);
#ifdef DEBUG
((long *) p)[1] = 0x55555555;
#endif /* DEBUG */
p = free_blocks[lowest];
/* it was bigger than we needed, break off a piece. */
if (lowest_len > plen) {
unsigned char *fp = free_blocks[lowest];
fp += plen;
obj_setlf(fp, (lowest_len - plen), 1);
#ifdef DEBUG
((long *) fp)[1] = 0x55555555;
#endif /* DEBUG */
if (prev <= free_blocks[lowest]) {
free_blocks[lowest] = 0;
} else {
free_blocks[lowest] = fp;
}
} else {
free_blocks[lowest] = 0;
}
if (obj_free(prev)) {
next = prev;
prev = 0;
} else {
next = oldp;
}
} else {
/*
* no free slot found or it is the previous slot. See if prev
* is free, and if so, just slide this range back.
*/
if (prev && obj_free(prev)) {
memmove(prev, p, plen);
blocks_moved++;
next = prev + plen;
obj_setlf(next, (p - prev), 1);
#ifdef DEBUG
((long *) next)[1] = 0x55555555;
#endif /* DEBUG */
p = prev;
if (prev_block >= 0) {
free_blocks[prev_block] = 0;
}
} else {
prev = p;
}
}
} else {
prev = p;
}
if (next > p && !obj_free(p)) {
REVERSE_SWAP(p);
}
p = next;
}
#ifdef TRACEGC
if (tracegc && blocks_moved) {
fprintf(stderr, "<GC: compacted %d objects>\n", blocks_moved);
}
#endif /* TRACEGC */
sysAssert(last_free == 0 || obj_free(last_free));
*last_freep = last_free;
return async_call ? blocks_moved : 0;
}
/*
* Garbage collect memory. If 'async_call' is true, this function is being
* called from the asych GC thread, and should immediately exit whenever
* the INTERRUPTS_PENDING() macro returns true. The 'free_space_goal' is
* the amount of memory in a single block which the caller is interested
* in having free. If such a block does not exist, compact the heap.
*
* gc0_locked() should only be called when holding the heap lock and the
* finalization queue locks. gc() or realObjAlloc() acquires the heap
* lock, then calls gc0() which grabs the finalization locks and calls
* gc_locked(), which does system-specific tasks then calls gc0_locked().
*/
unsigned char *
gc0_locked(int async_call, unsigned int free_space_goal)
{
/* SPARC only: "ta ST_FLUSH_WINDOWS" done by "gc" stub in machgc.s */
int do_compact_heap;
long start_time = now();
unsigned char *last_free = 0;
sysAssert(HEAP_LOCKED());
sysAssert(HASFINALQ_LOCKED());
sysAssert(FINALMEQ_LOCKED());
sysAssert(QUEUE_LOCKED());
if (verbosegc) {
nfreed = 0;
bytesfreed = 0;
}
memset((char *) markbits, 0, marksize);
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(GCTHREAD);
scanThreads();
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(GCCLASS);
scanClasses();
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(GCHEAP);
scanHeap();
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(GCHANDLES);
scanHandles();
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(GCFINAL);
prepareFinalization();
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(GCSWEEP);
do_compact_heap = freeSweep(free_space_goal);
if (async_call && INTERRUPTS_PENDING()) {
goto unlock;
}
MARK(COMPACT);
if (do_compact_heap) {
while (compactHeap(async_call, &last_free)) ;
}
if (async_call && INTERRUPTS_PENDING()) {
goto unlock; /* I.e. don't print <GC(async):... message */
}
if (verbosegc) {
long total_time = now() - start_time;
fprintf(stderr,
"<GC%s: freed %d objects, %d bytes in %d msec, %d%% free>\n",
async_call ? "(async)" : "",
nfreed, bytesfreed, total_time,
100 * FreeObjectCtr / TotalObjectCtr);
}
unlock:
if (java_monitor && !async_call) {
ExecEnv *ee = EE();
if (ee && ee->current_frame && ee->current_frame->current_method) {
java_mon(ee->current_frame->current_method,
(struct methodblock *)-1, now() - start_time);
}
}
return last_free;
}
/*
* Interface to synchronous garbage collection from other memory manage-
* ment code. This is called call when memory allocation fails. We
* expect to have the heap lock when it is called.
*/
unsigned char *gc_locked(int, unsigned int); /* Call holding locks! */
unsigned char *
gc0(int async_call, unsigned int free_space_goal)
{
unsigned char *last_free = 0;
sysAssert(HEAP_LOCKED());
HASFINALQ_LOCK();
FINALMEQ_LOCK();
QUEUE_LOCK();
if (sysThreadSingle() == SYS_OK) {
last_free = gc_locked(async_call, free_space_goal);
}
sysThreadMulti();
/* Notify the finalizer thread if objects pending finalization */
if (FinalizeMeQ) {
FINALMEQ_NOTIFY();
}
heap_memory_changes = 0;
QUEUE_UNLOCK();
FINALMEQ_UNLOCK();
HASFINALQ_UNLOCK();
return last_free;
}
/*
* User interface to synchronous garbage collection. This is called
* by an explicit call to GC. Asynchronous GC has its own entry point.
* At the call we do not already have the heap lock, so grab it.
*/
void
gc(int async_call, unsigned int free_space_goal)
{
HEAP_LOCK();
(void) gc0(async_call, free_space_goal);
HEAP_UNLOCK();
}
/*
* Called whenever the system thinks it is idle, and might want to
* do a GC. This might also want to grow the heap spaces to maintain
* the desired free space.
*/
void
asyncGC()
{
int freemem = FreeObjectCtr;
int totalrefs, freedrefs;
HEAP_LOCK();
HASFINALQ_LOCK();
FINALMEQ_LOCK();
QUEUE_LOCK();
if (sysThreadSingle() != SYS_OK) {
goto unlock;
}
/*
* We currently don't try to expand asynchronously, so ignore the
* return value (last_free) of gc_locked().
*/
(void) gc_locked(1, ~0);
if (INTERRUPTS_PENDING()) {
goto unlock;
}
/* Don't want to zero this unless gc actually completed... */
heap_memory_changes = 0;
/*
* If we didn't free anything, and we hit the low water mark,
* try clearing some soft refs.
*/
if ((freemem == FreeObjectCtr) && (freemem < FreeMemoryLowWaterMark)) {
/* If we can't clear any, just return */
if (!(freedrefs =
clearRefPointers(1, FreeMemoryLowWaterMark, &totalrefs))) {
goto unlock;
}
if (verbosegc) {
fprintf(stderr, "<GC(async): Asynchronously zeroed %d of %d soft refs>\n",
freedrefs, totalrefs);
}
if (INTERRUPTS_PENDING()) {
goto unlock;
}
(void) gc_locked(1, ~0);
}
unlock:
sysThreadMulti();
/* Notify the finalizer thread if objects pending finalization */
if (FinalizeMeQ) {
FINALMEQ_NOTIFY();
}
QUEUE_UNLOCK();
FINALMEQ_UNLOCK();
HASFINALQ_UNLOCK();
HEAP_UNLOCK();
return;
}
/*
* Initialize structures supporting GC: allocate and zero the mark bits.
* [Should we be allocating the mark bits at GC time instead?]
*/
void
InitializeGC(int max, int min)
{
/*
* Map the mark bits array, whose size is defined by the maximum
* (mapped) memory, and commit to enough memory to map the committed
* heap.
*/
markmax = ((max/(OBJECTGRAIN*BITSPERMARK) + 1) * 2) * sizeof(*markbits);
markbits = (unsigned int *) sysMapMem(markmax, &markmax);
if (!markbits) {
out_of_memory(); /* Can't start up */
}
/* Could postpone commit until GC; see comment at expandMarkBits() */
marksize = ((min/(OBJECTGRAIN*BITSPERMARK) + 1) * 2) * sizeof(*markbits);
markbits = sysCommitMem(markbits, marksize, &marksize);
if (!markbits) {
out_of_memory(); /* Can't start up */
}
}
/*
* Initialize the Java heap. Argument max_request is the maximum heap
* memory we are prepared to devote to the heap, and min_request is the
* minimum memory and amount allocated on startup.
*/
#define BACKOFF_FACTOR 0.75
void
InitializeAlloc(long max_request, long min_request)
{
long max, min;
/*
* Sanity check the initial requests: enforce constraint that
* max_request >= min_request. For backward compatibility, min_request
* overrides max_request. REMIND: this still allows us to try to start
* up with ridiculous values, but enforcing reasonable values is
* machine-dependent and must be done elsewhere.
*/
if (max_request < min_request) {
max_request = min_request;
}
/*
* Allocate the initial heap memory. In the abstract we first map
* the maximum we are interested in, then commit to the minimum/initial
* amount. Note that as actually implemented on some platforms,
* commitment might be a no-op, min might equal max etc.
*
* Because we don't really need more than min_request to start up,
* we can back off on the heap mapping if it initially fails. There
* is no guarantee that we will be allowed to expand to the max anyhow.
* Related spaces (e.g. mark bits) scale off what we actually get.
* We should back off in fairly large chunks so if we are successful
* we'll be able to map the rest of what we'll need.
*/
sysAssert(hpool == 0);
heapbase = (unsigned char *) sysMapMem(max_request, &max);
while (!heapbase) {
max_request -= (max_request * BACKOFF_FACTOR);
if (max_request < min_request) {
out_of_memory(); /* Can't start up */
}
heapbase = (unsigned char *) sysMapMem(max_request, &max);
}
heaptop = heapbase + max;
/*
* We want same partition proportions (20%/80%) of the mapped
* memory as the committed memory. The handle pool base must be
* HANDLEGRAIN aligned within the committed memory.
*/
hpool = heapbase + (int) ((heaptop - heapbase) * 0.20);
hpool = hpool - (int) (min_request * 0.20);
hpool = sysCommitMem(hpool, min_request, &min);
if (!hpool) {
out_of_memory(); /* Can't start up */
}
hpool = (unsigned char *)
(((int) hpool + HANDLEGRAIN-1) & ~(HANDLEGRAIN-1));
hpoollimit = hpool + min;
/*
* Allocate the initial GC mark bits array
*/
InitializeGC(max, min);
/*
* Leave the bottom 20% of the pool for handles, the rest for handles
*/
opoollimit = hpoollimit - sizeof(hdr);
hpoollimit = hpool +
((int) ((hpoollimit - hpool) * 0.20) & ~(HANDLEGRAIN - 1));
/*
* The object pool base must be sizeof(hdr) past OBJECTGRAIN alignment
*/
opool = hpoollimit;
while (((int) opool + sizeof(hdr)) & (OBJECTGRAIN - 1)) {
opool += sizeof(hdr);
}
hpoolhand = hpool;
/*
* Make sure that all the handles are zero. We recognize a free handle
* by the fact that its ->obj field is 0.
*/
memset(hpoolhand, 0, hpoollimit - hpoolhand);
obj_setlf(opool, opoollimit - opool, 1);
#ifdef DEBUG
((long *) opool)[1] = 0x55555555;
#endif /* DEBUG */
obj_setlf(opoollimit, 0, 0);
FreeObjectCtr = opoollimit - opool;
TotalObjectCtr = FreeObjectCtr;
FreeHandleCtr = hpoollimit - hpool;
TotalHandleCtr = FreeHandleCtr;
/*
* The free memory low water mark is used when deciding whether to
* free refs.
*/
FreeMemoryLowWaterMark = (long) (0.2 * FreeObjectCtr) & ~(OBJECTGRAIN - 1);
opoolhand = opool;
_hasfinalq_lock = (sys_mon_t *) sysMalloc(sysMonitorSizeof());
memset(_hasfinalq_lock, 0, sysMonitorSizeof());
HASFINALQ_LOCK_INIT();
_heap_lock = (sys_mon_t *) sysMalloc(sysMonitorSizeof());
memset((char *) _heap_lock, 0, sysMonitorSizeof());
HEAP_LOCK_INIT();
}
/*
* Miscellaneous stuff
*/
/*
* Support for DumpMonitors(): we want to print whose monitor this is
* if it is associated with an object. To check whether that's so we
* need to see whether the handle is in the handle pool, and the limits
* of the handle pool aren't visible out of gc.c.
*/
void
monitorDumpHelper(monitor_t *mid, void *name)
{
TID t;
unsigned int key = mid->key;
SetLimits();
if (mid && (mid->flags & MON_IN_USE) != 0) {
if (name == 0) {
if (ValidHandle(key)) {
name = Object2CString((JHandle *) key);
} else {
name = "unknown key";
}
}
fprintf(stderr, " %s (key=0x%x): ", name, key);
sysMonitorDumpInfo((sys_mon_t *)&mid->mid);
}
return;
}
/*
* Profiling support
*/
#define MAX_CLASS_HASH 1023
struct arrayinfo arrayinfo[] = {
{ 0, 0, "N/A", 0},
{ 0, 0, "N/A", 0},
{ T_CLASS, SIGNATURE_CLASS, "class[]", sizeof(OBJECT)},
{ 0, 0, "N/A", 0},
{ T_BOOLEAN, SIGNATURE_BOOLEAN, "bool[]", sizeof(char)},
{ T_CHAR, SIGNATURE_CHAR, "char[]", sizeof(unicode)},
{ T_FLOAT, SIGNATURE_FLOAT, "float[]", sizeof(float)},
{ T_DOUBLE, SIGNATURE_DOUBLE, "double[]", sizeof(double)},
{ T_BYTE, SIGNATURE_BYTE, "byte[]", sizeof(char)},
{ T_SHORT, SIGNATURE_SHORT, "short[]", sizeof(short)},
{ T_INT, SIGNATURE_INT, "int[]", sizeof(int)},
{ T_LONG, SIGNATURE_LONG, "long[]", sizeof(int64_t)},
#ifdef NO_LONGER_SUPPORTED
{ T_UBYTE, SIGNATURE_BYTE, "ubyte[]", sizeof(char)},
{ T_USHORT, SIGNATURE_SHORT, "ushort[]", sizeof(unsigned short)},
{ T_UINT, SIGNATURE_INT, "uint[]", sizeof(int)},
{ T_ULONG, SIGNATURE_LONG, "ulong[]", sizeof(int64_t)},
#endif
};
#define ARRAYTYPES (sizeof(arrayinfo) / sizeof(arrayinfo[0]))
void
profHandles(FILE *fp) {
SetLimits();
register JHandle *hp = (JHandle *) hpmin;
ClassClass *cb;
struct hash_entry {
ClassClass *cb;
int count;
int arraycount;
int arraylength;
} *tab, *p;
int arraycount[ARRAYTYPES];
int arraylength[ARRAYTYPES];
int handles_count = (JHandle *)hpmax - (JHandle *)hpmin;
int handles_used = 0;
int i;
/* array type mapping */
int tmap[64];
for (i = 0 ; i < ARRAYTYPES ; i++) {
tmap[arrayinfo[i].index] = i;
}
tab = (struct hash_entry *)sysMalloc(sizeof(struct hash_entry) * MAX_CLASS_HASH);
memset((char *)tab, 0, sizeof(struct hash_entry) * MAX_CLASS_HASH);
memset((char *)arraycount, 0, sizeof(arraycount));
memset((char *)arraylength, 0, sizeof(arraylength));
for (; hp <= (JHandle *) hpmax ; hp++) {
if ((hp->obj == 0) || (obj_free((unsigned char *)unhand(hp)))) {
continue;
}
switch (obj_flags(hp)) {
case T_NORMAL_OBJECT:
cb = obj_classblock(hp);
p = &tab[(((long)cb) >> 2) % MAX_CLASS_HASH];
while ((p->cb != 0) && (p->cb != cb)) {
if (p-- == tab) {
p = &tab[MAX_CLASS_HASH - 1];
}
}
if (p->cb == 0) {
p->cb = cb;
}
p->count++;
break;
case T_CLASS:
cb = ((ClassClass **)unhand((HArrayOfObject*)hp)->body)[obj_length(hp)];
p = &tab[(((long)cb) >> 2) % MAX_CLASS_HASH];
while ((p->cb != 0) && (p->cb != cb)) {
if (p-- == tab) {
p = &tab[MAX_CLASS_HASH - 1];
}
}
if (p->cb == 0) {
p->cb = cb;
}
p->arraycount++;
p->arraylength += obj_length(hp);
break;
default:
if (obj_flags(hp) < T_BOOLEAN) {
continue;
}
arraycount[tmap[obj_flags(hp)]]++;
arraylength[tmap[obj_flags(hp)]] += obj_length(hp);
break;
}
handles_used++;
}
fprintf(fp, "# handles-used, handles-free heap-used heap-free\n");
fprintf(fp, "%d %d %d %d\n", handles_used, handles_count - handles_used,
TotalObjectCtr - FreeObjectCtr, FreeObjectCtr);
fprintf(fp, "# type count bytes\n");
for (i = 0 ; i < ARRAYTYPES ; i++) {
if (arraycount[i]) {
fprintf(fp, "[%c %d %d\n", arrayinfo[i].sig, arraycount[i], arraylength[i] * arrayinfo[i].factor);
}
}
p = &tab[MAX_CLASS_HASH];
while (p-- != tab) {
if (p->cb != 0) {
if (p->count > 0) {
fprintf(fp, "L%s; %d %d\n",
classname(p->cb), p->count,
p->count * cbInstanceSize(p->cb));
}
if (p->arraycount > 0) {
fprintf(fp, "[L%s; %d %d\n", classname(p->cb), p->arraycount, p->arraylength * sizeof(OBJECT));
}
}
}
sysFree(tab);
}
void
prof_heap(FILE *fp)
{
gc(0, ~0);
profHandles(fp);
}
/*
* Debugging facilities and useful stuff to call from dbx...
*/
#ifdef DEBUG
void
DumpHeapInfo()
{
fprintf(stderr, "Heap layout info:\n");
fprintf(stderr, " Mapped memory:\t[0x%x,0x%x), size: 0x%x\n",
heapbase, heaptop, heaptop-heapbase);
fprintf(stderr, " Committed memory:\t[0x%x,0x%x), size: 0x%x\n",
hpool, opoollimit, opoollimit-hpool);
fprintf(stderr, " Handle space: \t[0x%x,0x%x], size: 0x%x\n",
hpool, hpoollimit, opool-hpool);
fprintf(stderr, " Object space: \t[0x%x,0x%x], size: 0x%x\n",
opool, opoollimit, opoollimit-opool+sizeof(hdr));
}
int
isMarked(char *p)
{
SetLimits();
return (IsMarked(p) ? 1 : 0);
}
int
isHandle(p)
{
SetLimits();
return (ValidHandle(p) ? 1 : 0);
}
int
isObject(p)
{
SetLimits();
return (ValidObject(p) ? 1 : 0);
}
int
isHorO(p)
{
SetLimits();
return (ValidHorO(p) ? 1 : 0);
}
int GCValidHandle(JHandle *h) {
SetLimits();
return ValidHandle(h) && h->obj != 0;
}
void
printPool(char *s)
{
SetLimits();
register unsigned char *p = opmin;
register col = 0;
char buf[100];
if (s) {
printf("\014%s\n", s);
}
while (p < opmax) {
if (++col > 7) {
putchar('\n'), col = 1;
}
if (obj_len(p) < 4 || obj_len(p) > (opoollimit-opool)) {
printf("Bogus length");
break;
}
printf("%d%c%c%c\t", obj_len(p),
p == opoolhand ? '*' : ' ',
obj_free(p) ? 'F' : ' ',
IsMarked(p + sizeof(hdr)) ? 'P' : ' ');
p += obj_len(p);
}
putchar('\n');
}
void
printHandles(char *s)
{
SetLimits();
JHandle *hp;
printf("\n\n%s\n", s);
for (hp = (JHandle *) hpool; hp <= (JHandle *) hpmax; hp++) {
if (hp->obj != 0) {
printf("%X %X%c\n", hp, hp->obj,
ValidObject(hp->obj) ? (IsMarked(hp->obj) ? 'P' : ' ') : '*');
}
}
printf("\n");
}
void
verifyObjectCtrs(void)
{
SetLimits();
register unsigned char *p = opmin;
int free = 0;
int inuse = 0;
int total = 0;
while (p < opmax) {
if (obj_free(p)) {
free += obj_len(p);
} else {
inuse += obj_len(p);
}
total += obj_len(p);
p += obj_len(p);
}
sysAssert(free == FreeObjectCtr);
sysAssert(inuse == TotalObjectCtr - FreeObjectCtr);
sysAssert(total == TotalObjectCtr);
/* For more verbose output:
fprintf(stderr, "verifyObjectCtrs() detected:\n");
fprintf(stderr, " %d bytes free versus %d bytes accumulated\n",
free, FreeObjectCtr);
fprintf(stderr, " %d bytes in use versus %d bytes accumulated\n",
inuse, TotalObjectCtr - FreeObjectCtr);
fprintf(stderr, " %d bytes total versus %d bytes accumulated\n",
total, TotalObjectCtr);
*/
}
void
verifyHandleCtrs(void)
{
SetLimits();
register JHandle *hp;
int free = 0;
int inuse = 0;
int total = 0;
for (hp = (JHandle *) hpool; hp <= (JHandle *) hpmax; hp++) {
if (hp->obj == 0) {
free += sizeof(JHandle);
} else {
inuse += sizeof(JHandle);
}
total += sizeof(JHandle);
}
sysAssert(free == FreeHandleCtr);
sysAssert(inuse == TotalHandleCtr - FreeHandleCtr);
sysAssert(total == TotalHandleCtr);
/* For more verbose output:
fprintf(stderr, "verifyHandleCtrs() detected:\n");
fprintf(stderr, " %d bytes free versus %d bytes accumulated\n",
free, FreeHandleCtr);
fprintf(stderr, " %d bytes in use versus %d bytes accumulated\n",
inuse, TotalHandleCtr - FreeHandleCtr);
fprintf(stderr, " %d bytes total versus %d bytes accumulated\n",
total, TotalHandleCtr);
*/
}
void
validatePool(long quick)
{
SetLimits();
register unsigned char *p = opmin;
register JHandle *hp;
int err = 0;
char buf[100];
while (p < opmax) {
sysAssert(obj_len(p) >= 4);
sysAssert(obj_len(p) < (opoollimit-opool));
if (obj_free(p)) {
sysAssert(((long *) p)[1] == 0x55555555);
} else if (!quick) {
for (hp = (JHandle *) hpool; hp <= (JHandle *) hpmax; hp++) {
if (hp->obj == (ClassObject *) (p + sizeof(hdr))) {
goto HasHandle;
}
}
fprintf(stderr, "@%X -- alloced object with no handle\n", p), err++;
}
HasHandle:
p += obj_len(p);
}
for (hp = (JHandle *) hpool; hp <= (JHandle *) hpmax; hp++) {
if (hp->obj)
if (ValidObject(hp->obj) && obj_free((char *) hp->obj - sizeof(hdr))) {
fprintf(stderr, "@%X/%X -- Handle points to free block\n", hp, hp->obj), err++;
} else {
switch (obj_flags(hp)) {
case T_NORMAL_OBJECT:
if (obj_methodtable(hp) == 0)
fprintf(stderr, "1 @%X/%X -- Invalid method table\n", hp, hp->methods), err++;
else if (obj_methodtable(hp) != cbMethodTable(obj_classblock(hp)))
fprintf(stderr, "2 @%X/%X -- Invalid method table\n", hp, hp->methods), err++;
break;
default:
if (obj_flags(hp) < T_CLASS )
fprintf(stderr, "3 @%X/%X -- Invalid method table\n", hp, hp->methods), err++;
}
}
}
fprintf(stderr, err == 0 ? "OK memory pool\n" : "***%d errors in the memory pool***\n", err);
}
#else
#define validatepool(a) 0
#endif /* DEBUG */
#ifdef NOTCURRENTLYUSED
void
objFree(HObject * o)
{
register unsigned char *p;
HEAP_LOCK();
#ifdef DEBUG
if (o == 0) {
printf("freeing null object\n");
sysAbort();
}
#endif /* DEBUG */
p = ((unsigned char *) unhand(o)) - sizeof(hdr);
#ifdef DEBUG
if (p > opoollimit || p < opool) {
printf("freeing object not in the pool\n");
sysAbort();
}
#endif /* DEBUG */
if (!obj_free(p)) {
FreeObjectCtr += obj_len(p);
obj_setfree(p);
unhand(o) = 0;
#ifdef DEBUG
((long *) p)[1] = 0x55555555;
#endif /* DEBUG */
}
HEAP_UNLOCK();
}
#endif /* NOTCURRENTLYUSED */