gecko-dev/ef/gc/src/smheap.c

528 строки
19 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*******************************************************************************
S P O R T M O D E L
_____
____/_____\____
/__o____o____o__\ __
\_______________/ (@@)/
/\_____|_____/\ x~[]~
~~~~~~~~~~~/~~~~~~~|~~~~~~~\~~~~~~~~/\~~~~~~~~~
Advanced Technology Garbage Collector
Copyright (c) 1997 Netscape Communications, Inc. All rights reserved.
Recovered by: Warren Harris
*******************************************************************************/
#include "smheap.h"
#include <string.h>
PRUint8* SM_CardTable;
SMHeap sm_Heap; /* _the_ global heap */
/******************************************************************************/
static PRBool gcInitialized = PR_FALSE;
SM_IMPLEMENT(PRStatus)
SM_InitGC(PRUword initialHeapSize, PRUword maxHeapSize,
SMGCHookProc beforeGCHook, void* beforeGCClosure,
SMGCHookProc afterGCHook, void* afterGCClosure,
SMMarkRootsProc markRootsProc, void* markRootsClosure)
{
PRStatus status;
PRUword i;
#ifndef SM_NO_WRITE_BARRIER
PRUword cardTableSize;
#endif
if (gcInitialized)
return PR_SUCCESS;
gcInitialized = PR_TRUE;
/* Make sure the page descriptor generation field is big enough. */
SM_ASSERT(SMGenNum_Free <= (1 << SM_PAGEDESC_GEN_BITS));
memset(&sm_Heap, 0, sizeof(SMHeap)); /* in case we fail */
#ifndef SM_NO_WRITE_BARRIER
cardTableSize = SM_PAGE_COUNT(initialHeapSize) * sizeof(SMCardDesc);
initialHeapSize += cardTableSize;
#endif
status = SM_InitMalloc(initialHeapSize, maxHeapSize);
if (status != PR_SUCCESS)
return status;
/* Initialize generations */
for (i = SMGenNum_Freshman; i <= SMGenNum_Static; i++) {
status = sm_InitGen(&sm_Heap.gen[i], SM_DEFAULT_ALLOC_THRESHOLD);
if (status != PR_SUCCESS) goto fail;
}
/* Initialize tables */
#ifndef SM_NO_WRITE_BARRIER
sm_Heap.cardTablePageCount = SM_PAGE_COUNT(cardTableSize);
sm_Heap.cardTableMem = (SMCardDesc*)sm_NewCluster(&sm_PageMgr,
sm_Heap.cardTablePageCount);
if (sm_Heap.cardTableMem == NULL) goto fail;
SM_CardTable = sm_Heap.cardTableMem - (SM_PAGE_NUMBER(sm_PageMgr.heapBase) * sizeof(SMCardDesc));
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
SMCardDesc* cd = &sm_Heap.cardTableMem[i];
SM_CARDDESC_INIT(cd);
}
#endif
SM_INIT_STACK(&sm_Heap.markStack);
sm_Heap.markRootsProc = markRootsProc;
sm_Heap.markRootsClosure = markRootsClosure;
sm_Heap.beforeGCHook = beforeGCHook;
sm_Heap.beforeGCClosure = beforeGCClosure;
sm_Heap.afterGCHook = afterGCHook;
sm_Heap.afterGCClosure = afterGCClosure;
sm_Heap.collectingGenNum = SMGenNum_Free;
status = sm_InitFinalizer();
if (status != PR_SUCCESS)
goto fail;
sm_CompactHook = sm_CollectAndFinalizeAll;
return PR_SUCCESS;
fail:
SM_CleanupGC(PR_FALSE);
return PR_FAILURE;
}
SM_IMPLEMENT(PRStatus)
SM_CleanupGC(PRBool finalizeOnExit)
{
int i;
PRStatus status;
status = sm_FiniFinalizer(finalizeOnExit);
/* do the following even if finalize-on-exit failed */
sm_CompactHook = NULL;
sm_DestroyStack(&sm_Heap.markStack);
for (i = SMGenNum_Freshman; i <= SMGenNum_Static; i++) {
sm_FiniGen(&sm_Heap.gen[i]);
}
#ifndef SM_NO_WRITE_BARRIER
sm_DestroyCluster(&sm_PageMgr,
(SMPage*)sm_Heap.cardTableMem,
sm_Heap.cardTablePageCount);
SM_CardTable = NULL;
#endif
SM_CleanupMalloc();
gcInitialized = PR_FALSE;
return status;
}
#if !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG)
SM_IMPLEMENT(void)
SM_DirtyPage(void* addr)
{
SMCardDesc* cd = &SM_CardTable[SM_PAGE_NUMBER(addr)];
SMCardDesc* cardTableLimit = (SMCardDesc*)
((char*)sm_Heap.cardTableMem + SM_PAGE_WIDTH(sm_Heap.cardTablePageCount));
SM_ASSERT(sm_Heap.cardTableMem <= cd && cd < cardTableLimit);
*cd = 0;
}
SM_IMPLEMENT(void)
SM_CleanPage(void* addr)
{
SMCardDesc* cd = &SM_CardTable[SM_PAGE_NUMBER(addr)];
SMCardDesc* cardTableLimit = (SMCardDesc*)
((char*)sm_Heap.cardTableMem + SM_PAGE_WIDTH(sm_Heap.cardTablePageCount));
SM_ASSERT(sm_Heap.cardTableMem <= cd && cd < cardTableLimit);
*cd = SMGenNum_Free + 1;
}
#endif /* !defined(SM_NO_WRITE_BARRIER) && defined(DEBUG) */
/******************************************************************************/
#ifdef SM_STATS
SM_IMPLEMENT(void)
SM_Stats(SMStats* stats)
{
SMPageCount i;
PRUword totalAmountRequested = 0, totalRequests = 0;
memset(stats, 0, sizeof(SMStats));
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
SMGenNum genNum = SM_PAGEDESC_GEN(pd);
if (genNum != SMGenNum_Free) {
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
if (bucket == SM_LARGE_OBJECT_BUCKET) {
SMSmallObjSize objSize = pd->allocCount * SM_PAGE_SIZE;
PRUword overhead = pd->allocCount * sizeof(SMPageDesc);
stats->perGen[genNum].amountUsed += objSize;
stats->perGen[genNum].overhead += overhead;
}
else {
SMSmallObjSize objSize = sm_ObjectSize[bucket];
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
PRUword amountUsed = pd->allocCount * objSize;
PRUword amountFree = (objsPerPage - pd->allocCount) * objSize;
PRUword overhead = sizeof(SMPageDesc)
+ objsPerPage /* object table */
+ SM_PAGE_SIZE - (objsPerPage * objSize); /* unused space at end of page */
stats->perGen[genNum].amountFree += amountFree;
stats->perGen[genNum].amountUsed += amountUsed;
stats->perGen[genNum].overhead += overhead;
}
}
}
stats->systemOverhead =
sizeof(SMHeap) +
sm_PageMgr.heapPageCount * sizeof(SMPageDesc) +
#ifndef SM_NO_WRITE_BARRIER
sm_PageMgr.heapPageCount * sizeof(SMCardDesc) +
#endif
sizeof(SMSmallObjSize[SM_ALLOC_BUCKETS]) + /* sm_ObjectSize */
sizeof(SMSmallObjCount[SM_ALLOC_BUCKETS]) + /* sm_ObjectsPerPage */
#ifndef SM_NO_TABLE_DIVISION
sizeof(SMSmallObjCount[SM_DIVTABLE_ENTRY_COUNT]) + /* sm_ObjectSizeDivTableMem */
sizeof(SMSmallObjCount*[SM_ALLOC_BUCKETS]) + /* sm_ObjectSizeDivTable */
#endif
sizeof(SMBucket[SM_ALLOC_BUCKETS - 1]); /* sm_AntiBucket */
stats->systemOverhead += sm_StackSize(&sm_Heap.markStack);
for (i = SMGenNum_Freshman; i <= SMGenNum_Malloc; i++) {
stats->total.amountFree += stats->perGen[i].amountFree;
stats->total.amountUsed += stats->perGen[i].amountUsed;
stats->total.overhead += stats->perGen[i].overhead;
if (i < SMGenNum_Malloc)
stats->perGen[i].collections = sm_Heap.gen[i].collectCount;
stats->total.collections += stats->perGen[i].collections;
if (i < SMGenNum_Malloc) {
stats->total.totalAllocated +=
stats->perGen[i].totalAllocated = sm_Heap.gen[i].pool.totalAllocated;
stats->total.totalRequested +=
stats->perGen[i].totalRequested = sm_Heap.gen[i].pool.totalRequested;
}
}
stats->total.overhead += stats->systemOverhead;
}
static void
sm_DumpStat(FILE* out, char* name, PRUword used, PRUword free, PRUword over,
PRUword totalAllocated, PRUword totalRequested, PRUword collections)
{
PRUword total = used + free + over;
fprintf(out, " %-8s %10u (%2u%%) %10u (%2u%%) %10u (%2u%%) ",
name,
used, (total ? (used * 100 / total) : 0),
free, (total ? (free * 100 / total) : 0),
over, (total ? (over * 100 / total) : 0),
collections);
if (totalAllocated) {
PRUword frag = (totalAllocated
? (totalAllocated - totalRequested) * 100 / totalAllocated
: 0);
fprintf(out, " %3u%%", frag);
}
else
fprintf(out, " ");
if (collections)
fprintf(out, " %7u", collections);
fprintf(out, "\n");
}
SM_IMPLEMENT(void)
SM_DumpStats(FILE* out, PRBool detailed)
{
int i;
char* genName[SM_GEN_COUNT] =
{ " Gen 0", " Gen 1", " Gen 2", " Gen 3", " Static" };
PRUword gcUsed = 0, gcFree = 0, gcOver = 0;
PRUword gcTotalAllocated = 0, gcTotalRequested = 0;
PRUword used, free, over, frag;
SMStats stats;
SM_Stats(&stats);
fprintf(out, "--SportModel-Statistics-----------------------------------------------------\n");
fprintf(out, " SPACE USED FREE OVERHEAD FRAG COLLECT\n");
for (i = SMGenNum_Freshman; i < SMGenNum_Malloc; i++) {
used = stats.perGen[i].amountUsed;
free = stats.perGen[i].amountFree;
over = stats.perGen[i].overhead;
if (detailed) {
sm_DumpStat(out, genName[i], used, free, over,
stats.perGen[i].totalAllocated, stats.perGen[i].totalRequested,
stats.perGen[i].collections);
}
gcUsed += used;
gcFree += free;
gcOver += over;
gcTotalAllocated += stats.perGen[i].totalAllocated;
gcTotalRequested += stats.perGen[i].totalRequested;
}
frag = (gcTotalAllocated
? (gcTotalAllocated - gcTotalRequested) * 100 / gcTotalAllocated
: 0);
sm_DumpStat(out, "GC Total", gcUsed, gcFree, gcOver,
gcTotalAllocated, gcTotalRequested,
stats.perGen[SMGenNum_Freshman].collections);
used = stats.perGen[SMGenNum_Malloc].amountUsed;
free = stats.perGen[SMGenNum_Malloc].amountFree;
over = stats.perGen[SMGenNum_Malloc].overhead;
sm_DumpStat(out, "Malloc", used, free, over,
stats.perGen[SMGenNum_Malloc].totalAllocated,
stats.perGen[SMGenNum_Malloc].totalRequested,
0);
used = stats.total.amountUsed;
free = stats.total.amountFree;
over = stats.total.overhead;
if (detailed) {
sm_DumpStat(out, " System", 0, 0, stats.systemOverhead, 0, 0, 0);
}
sm_DumpStat(out, "TOTAL", used, free, over,
gcTotalAllocated + stats.perGen[SMGenNum_Malloc].totalAllocated,
gcTotalRequested + stats.perGen[SMGenNum_Malloc].totalRequested,
0);
fprintf(out, "----------------------------------------------------------------------------\n");
}
#endif /* SM_STATS */
/******************************************************************************/
#ifdef DEBUG
SM_IMPLEMENT(void)
SM_SetCollectThresholds(PRUword gen0Threshold, PRUword gen1Threshold,
PRUword gen2Threshold, PRUword gen3Threshold,
PRUword staticGenThreshold)
{
if (gen0Threshold != 0)
sm_Heap.gen[SMGenNum_Freshman].allocThreshold = gen0Threshold;
if (gen1Threshold != 0)
sm_Heap.gen[SMGenNum_Sophomore].allocThreshold = gen1Threshold;
if (gen2Threshold != 0)
sm_Heap.gen[SMGenNum_Junior].allocThreshold = gen2Threshold;
if (gen3Threshold != 0)
sm_Heap.gen[SMGenNum_Senior].allocThreshold = gen3Threshold;
if (staticGenThreshold != 0)
sm_Heap.gen[SMGenNum_Static].allocThreshold = staticGenThreshold;
}
#endif /* DEBUG */
#ifdef SM_VERIFY
#if SM_VERIFY == 2
static void
sm_VerifyRange(SMObject** refs, PRUword count)
{
PRUword i;
for (i = 0; i < count; i++) {
SMObject* obj = refs[i];
if (obj) {
SMObjStruct* dobj = SM_DECRYPT_OBJECT(obj);
SMPageDesc* pd = SM_OBJECT_PAGEDESC(dobj);
SMObjDesc* od = SM_OBJECT_HEADER_FROM_PAGEDESC(dobj, pd);
SM_ASSERT(SM_OBJDESC_IS_MARKED(od));
SM_ASSERT(SM_OBJECT_CLASS(obj));
}
}
}
#endif
extern SMPool sm_MallocPool;
void
sm_VerifyHeap(void)
{
SMGenNum genNum;
SMPageCount i;
//SM_ACQUIRE_MONITORS();
//PR_SuspendAll();
for (i = 0; i < sm_PageMgr.heapPageCount; i++) {
SMPageDesc* pd = &sm_PageMgr.pageTableMem[i];
genNum = SM_PAGEDESC_GEN(pd);
if (genNum == SMGenNum_Free) {
SM_ASSERT(pd->next == NULL);
SM_ASSERT(pd->objTable == NULL);
SM_ASSERT(pd->allocCount == 0);
SM_ASSERT(pd->largeObjDesc.flags == 0);
}
else {
SMBucket bucket = SM_PAGEDESC_BUCKET(pd);
SMSmallObjCount objsPerPage = sm_ObjectsPerPage[bucket];
SMPageDesc* pd2;
SMSmallObjCount count = 0;
SMSmallObjCount j;
SMPool* pool = (genNum == SMGenNum_Malloc)
? &sm_MallocPool
: &sm_Heap.gen[genNum].pool;
if (bucket == SM_LARGE_OBJECT_BUCKET) {
SM_ASSERT(pd->objTable == &pd->largeObjDesc);
SM_ASSERT(SM_PAGEDESC_IS_LARGE_OBJECT_START(pd));
SM_ASSERT(pd->allocCount != 0);
pd2 = pd;
count = pd->allocCount;
while (--count > 0) {
i++;
pd2++;
SM_ASSERT(SM_PAGEDESC_GEN(pd2) == genNum);
SM_ASSERT(SM_PAGEDESC_BUCKET(pd2) == bucket);
SM_ASSERT(SM_PAGEDESC_LARGE_OBJECT_START(pd2) == pd);
SM_ASSERT(pd2->objTable == &pd->largeObjDesc);
SM_ASSERT(!SM_PAGEDESC_IS_LARGE_OBJECT_START(pd2));
}
}
else {
SMObjDesc* od = pd->objTable;
for (j = 0; j < objsPerPage; j++) {
if (SM_OBJDESC_IS_ALLOCATED(od)) {
SM_ASSERT((od->flags & SM_OBJDESC_STATE_MASK) == SMObjectState_Marked);
count++;
}
else {
SM_ASSERT(SM_OBJDESC_IS_FREE(od));
}
od++;
}
SM_ASSERT(pd->allocCount == count);
pd2 = pool->sweepList[bucket].sweepPage;
while (pd2 != NULL) {
if (pd2 == pd) {
SM_ASSERT(count <= objsPerPage);
goto next;
}
pd2 = pd2->next;
}
/* If we get here, there wasn't a pagedesc in the sweep list.
Figure out if it's somewhere else. */
{
SMGenNum g;
for (g = SMGenNum_Freshman; g <= SMGenNum_Malloc; g++) {
SMBucket b;
for (b = 0; b < SM_ALLOC_BUCKETS; b++) {
SMPool* pool = (g == SMGenNum_Malloc)
? &sm_MallocPool
: &sm_Heap.gen[g].pool;
SMPageDesc* pd2 = pool->sweepList[b].sweepPage;
while (pd2 != NULL) {
if (!(g == genNum && b == bucket)) {
SM_ASSERT(pd != pd2);
}
pd2 = pd2->next;
}
}
}
}
/* If the page desc wasn't found anywhere, then it better be full. */
SM_ASSERT(count == objsPerPage);
next:;
/* continue */
}
#if SM_VERIFY == 2
/* Verify that the gc objects on the page have all their fields marked */
if (SM_IS_GC_SPACE(genNum)) {
SMObjDesc* od = pd->objTable;
SMObjStruct* obj = (SMObjStruct*)SM_PAGEDESC_PAGE(pd);
SMSmallObjSize objSize = sm_ObjectSize[bucket];
for (j = 0; j < objsPerPage; j++) {
if (SM_OBJDESC_IS_MARKED(od)) {
SMClass* clazz = SM_OBJECT_CLASS(obj);
switch (SM_CLASS_KIND(clazz)) {
case SMClassKind_ObjectClass: {
SMFieldDesc* fieldDesc = SM_CLASS_GET_INST_FIELD_DESCS(clazz);
PRUint16 k, fdCount = SM_CLASS_GET_INST_FIELDS_COUNT(clazz);
for (k = 0; k < fdCount; k++) {
PRWord offset = fieldDesc[k].offset;
PRUword count = fieldDesc[k].count;
SMObject** fieldRefs = &SM_OBJECT_FIELDS(obj)[offset];
sm_VerifyRange(fieldRefs, count);
}
break;
}
case SMClassKind_ArrayClass: {
if (SM_IS_OBJECT_ARRAY_CLASS(clazz)) {
PRUword size = SM_ARRAY_SIZE(obj);
SMObject** elementRefs = SM_ARRAY_ELEMENTS(obj);
sm_VerifyRange(elementRefs, size);
}
break;
}
default:
break;
}
}
obj = (SMObjStruct*)((char*)obj + objSize);
od++;
}
}
#endif
}
}
/* also verify that all the sweep lists are consistent */
for (genNum = SMGenNum_Freshman; genNum <= SMGenNum_Static; genNum++) {
SMBucket bucket;
for (bucket = 0; bucket < SM_ALLOC_BUCKETS; bucket++) {
SMSweepList* sweepList = &sm_Heap.gen[genNum].pool.sweepList[bucket];
SMPageDesc* pd = sweepList->sweepPage;
SMSmallObjCount i;
for (i = 0; i < sweepList->sweepIndex; i++) {
SM_ASSERT(!SM_OBJDESC_IS_FREE(&pd->objTable[i]));
}
for (; pd != NULL; pd = pd->next) {
SM_ASSERT(SM_PAGEDESC_BUCKET(pd) == bucket);
SM_ASSERT(SM_PAGEDESC_GEN(pd) == genNum);
}
}
}
//SM_RELEASE_MONITORS();
//PR_ResumeAll();
}
#endif /* SM_VERIFY */
/******************************************************************************/