📄 ejsgarbage.c
字号:
/* * @file ejsGarbage.c * @brief EJS Garbage collector. * @overview This implements a generational mark and sweep collection scheme. *//********************************* Copyright **********************************//* * @copy default * * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved. * * This software is distributed under commercial and open source licenses. * You may use the GPL open source license described below or you may acquire * a commercial license from Mbedthis Software. You agree to be fully bound * by the terms of either license. Consult the LICENSE.TXT distributed with * this software for full details. * * This software is open source; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See the GNU General Public License for more * details at: http://www.mbedthis.com/downloads/gplLicense.html * * This program is distributed WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * This GPL license does NOT permit incorporating this software into * proprietary programs. If you are unable to comply with the GPL, you must * acquire a commercial license to use this software. Commercial licenses * for this software and support services are available from Mbedthis * Software at http://www.mbedthis.com * * @end *//********************************** Includes **********************************/#include "ejs.h"#if BLD_FEATURE_EJS/****************************** Forward Declarations **************************/static void mark(Ejs *ep);static void markObjByVar(Ejs *ep, EjsVar *op);static void markObj(EjsObj *obj);static void markPerm(Ejs *ep, uint gen);static int sweep(Ejs *ep, uint gen);static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex);static void ejsGracefulDegrade(Ejs *ep);static void resetMarks(Ejs *ep, EjsSlab *slab);#if FUTUREstatic void ageGenerations(Ejs *ep);#endif#if BLD_DEBUG && (!BREW || BREW_SIMULATOR)uint breakAddr;#endif/************************************* Code ***********************************/void ejsGCInit(Ejs *ep, int objInc, int propInc, int varInc, int strInc){ EjsSlab *slab; if (ep->service && ep->service->globalClass) { ep->service->globalClass->objectState->gcMarked = 1; } slab = &ep->slabs[EJS_SLAB_OBJ]; slab->allocIncrement = objInc; slab->size = EJS_ALLOC_ALIGN(sizeof(EjsObj)); slab = &ep->slabs[EJS_SLAB_PROPERTY]; slab->allocIncrement = propInc; slab->size = EJS_ALLOC_ALIGN(sizeof(EjsProperty)); slab = &ep->slabs[EJS_SLAB_VAR]; slab->allocIncrement = varInc; slab->size = EJS_ALLOC_ALIGN(sizeof(EjsVar)); /* * Initialize GC. * Enable GC both idle and demand collections. * Set no limits and garbage collect if the slabs are * empty and we have used more than the THRESHOLD of ram. */ ep->gc.debugLevel = 0; ep->gc.enable = 1; ep->gc.enableIdleCollect = 1; ep->gc.enableDemandCollect = 1; ep->gc.workQuota = EJS_GC_WORK_QUOTA; ep->gc.maxMemory = 0;}/******************************************************************************/#if BLD_FEATURE_ALLOC_STATSvoid ejsPrintAllocReport(Ejs *ep, bool printLeakReport){ EjsSlab *slab; char *name; int slabIndex, isObj; for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { slab = &ep->slabs[slabIndex]; if (slabIndex == EJS_SLAB_VAR) { name = "var"; } else if (slabIndex == EJS_SLAB_PROPERTY) { name = "prop"; } else { name = "obj"; } mprLog(ep, 0, " "); mprLog(ep, 0, " GC \"%s\" local slab", name); mprLog(ep, 0, " Total blocks %14d", slab->allocCount + slab->freeCount); mprLog(ep, 0, " Block size %14d", slab->size); mprLog(ep, 0, " Slab RAM allocated %14d", (slab->allocCount + slab->freeCount) * slab->size); mprLog(ep, 0, " Slab RAM in use %14d", slab->allocCount * slab->size); mprLog(ep, 0, " Blocks in use %14d", slab->allocCount); mprLog(ep, 0, " Free blocks %14d", slab->freeCount); mprLog(ep, 0, " Peak allocated %14d", slab->peakAllocated); mprLog(ep, 0, " Peak free %14d", slab->peakFree); mprLog(ep, 0, " Total allocations %14d", slab->totalAlloc); mprLog(ep, 0, " Total blocks reclaimed %14d", slab->totalReclaimed); mprLog(ep, 0, " Total sweeps %14d", slab->totalSweeps); mprLog(ep, 0, " Allocation inc %14d", slab->allocIncrement); } mprLog(ep, 0, " "); mprLog(ep, 0, " Total EJS memory in use %10d", ejsGetUsedMemory(ep)); mprLog(ep, 0, " Total EJS memory allocated %10d", ejsGetAllocatedMemory(ep)); if (printLeakReport) { mprLog(ep, 0, " "); for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) { int size; slab = &ep->slabs[slabIndex]; isObj = 0; mprLog(ep, 0, " "); if (slabIndex == EJS_SLAB_VAR) { name = "var"; size = sizeof(EjsVar); } else if (slabIndex == EJS_SLAB_PROPERTY) { name = "prop"; size = sizeof(EjsProperty); } else { name = "obj"; size = sizeof(EjsObj); isObj++; }#if BLD_FEATURE_ALLOC_LEAK_TRACK{ EjsGCLink *lp; EjsObj *obj; int count; mprLog(ep, 0, "EJS Leak Report for \"%s\"", name); count = 0; for (lp = slab->allocList[0].next; lp; lp = lp->next) { mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, size); if (isObj) { obj = (EjsObj*) lp; mprLog(ep, 0, " %-20s %10d %s %s", lp->allocatedBy, size, obj->permanent ? "permanent" : "", obj->alive ? "alive" : "" ); } else { mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, size); } count++; } mprLog(ep, 0, " Total blocks %14d", count);}#endif } mprLog(ep, 0, " "); }}#endif/******************************************************************************//* * Slab allocator */static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex){ EjsSlab *slab; EjsGCLink *block; EjsGC *gc; uint allocatedMemory; int i; mprStackCheck(ep); if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { mprAssert(0); return 0; } /* * See if the slab has some free blocks */ slab = &ep->slabs[slabIndex]; if ((block = slab->freeList.next) == 0) { allocatedMemory = ejsGetAllocatedMemory(ep); gc = &ep->gc; /* * No blocks available. If demand collection is enabled, try * to garbage collect first. We collect if we have done a good * work quota or we are over the max memory limit. */ if (slabIndex != EJS_SLAB_VAR && ep->gc.enable && ep->gc.enableDemandCollect) { if ((ep->gc.workDone > ep->gc.workQuota) || (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory)) {#if DEBUG_USE_ONLY if (ep->gc.debugLevel > 0) { mprLog(ep, 0, "Need GC, EJS RAM %d, MPR RAM %d\n", allocatedMemory, mprGetAllocatedMemory(ep)); if (ep->gc.debugLevel > 4) { ejsPrintAllocReport(ep, 0); } }#endif if (ejsCollectGarbage(ep, slabIndex) == 0) { block = slab->freeList.next; } } } if (block == 0) { if (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory) { /* * We are above the max memory limit. We will fail this * memory allocation, but allow subsequent allocations to * permit error recovery. We gracefully degrade by setting * slab chunk sizes to 1. This minimizes real memory * consumption. This allows us to create * an exception block to be created by upper layers. */ if (! gc->degraded) { ejsGracefulDegrade(ep); return 0; } } /* * Still non available, so allocate more memory for a set of blocks * OPT -- should bypass mprAlloc. Need mprMalloc. */ block = mprAlloc(ep->slabAllocContext, slab->size * slab->allocIncrement); if (block == 0) { /* * Now we're in trouble. We should really never get here * as the graceful degrade will have signaled a memory * allocation failure. */ mprAssert(block != 0); return 0; } /* * Chain all the blocks together onto the slab free list */ for (i = slab->allocIncrement - 1; i >= 0; i--) { block->next = slab->freeList.next;#if BLD_DEBUG block->magic = EJS_MAGIC_FREE;#endif slab->freeList.next = block; block = (EjsGCLink*) ((char*) block + slab->size); } block = slab->freeList.next;#if BLD_FEATURE_ALLOC_STATS slab->freeCount += slab->allocIncrement; if (slab->freeCount > slab->peakFree) { slab->peakFree = slab->freeCount; }#endif } } /* * We use block to point to the user data in the block. We only * store the magic number (if debug). No other data is stored in the * user block. */#if BLD_DEBUG mprAssert(block->magic == EJS_MAGIC_FREE);#endif /* * Remove from the free list */ slab->freeList.next = block->next; /* * Zero block */ memset(block, 0, slab->size);#if BLD_DEBUG block->magic = EJS_MAGIC;#endif#if BLD_FEATURE_ALLOC_STATS slab->totalAlloc++; if (++slab->allocCount > slab->peakAllocated) { slab->peakAllocated = slab->allocCount; } slab->freeCount--;#endif#if BLD_DEBUG && (!BREW || BREW_SIMULATOR) if ((uint) block == breakAddr) { mprBreakpoint(MPR_LOC, "Watched Block"); }#endif return block;}/******************************************************************************/EjsObj *ejsAllocObj(EJS_LOC_DEC(ep, loc)){ EjsObj *obj; EjsSlab *slab; obj = (EjsObj*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_OBJ); /* * Add to the allocated block list for the New generation. */ if (obj) { slab = &ep->slabs[EJS_SLAB_OBJ]; obj->gc.next = slab->allocList[EJS_GEN_NEW].next;#if BLD_FEATURE_ALLOC_LEAK_TRACK obj->gc.allocatedBy = loc;#endif obj->ejs = ep; slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) obj; ep->gc.workDone++; } return obj;}/******************************************************************************/EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ep, loc)){ EjsProperty *prop; prop = (EjsProperty*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_PROPERTY); mprAssert(prop); if (prop) { prop->var.type = EJS_TYPE_NULL; prop->var.isProperty = 1;#if BLD_FEATURE_ALLOC_LEAK_TRACK prop->var.gc.allocatedBy = loc;#endif } return prop;}/******************************************************************************/EjsVar *ejsAllocVar(EJS_LOC_DEC(ep, loc)){ EjsVar *vp; vp = (EjsVar*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_VAR); mprAssert(vp); if (vp) {#if BLD_FEATURE_ALLOC_LEAK_TRACK EjsSlab *slab; vp->gc.allocatedBy = loc; slab = &ep->slabs[EJS_SLAB_VAR]; vp->gc.next = slab->allocList[EJS_GEN_NEW].next; slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) vp;#endif#if BLD_DEBUG vp->propertyName = 0;#endif } return vp;}/******************************************************************************//* * Return the block back to the relevant slab */void ejsFree(Ejs *ep, void *ptr, int slabIndex){ EjsSlab *slab; EjsGCLink *block; mprAssert(ep); mprAssert(ptr); if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) { mprAssert(slabIndex >= 0 && slabIndex < EJS_SLAB_MAX); return; } slab = &ep->slabs[slabIndex];#if BLD_FEATURE_ALLOC_LEAK_TRACK if (slabIndex == EJS_SLAB_VAR) { EjsVar *vp, *np, *prev; /* * Remove the block rom the alloc list. WARNING: this is slow * and should not be used in production code. */ vp = (EjsVar*) ptr; prev = 0; for (np = (EjsVar*) slab->allocList[0].next; np; np = (EjsVar*) np->gc.next) { if (vp == np) { if (prev) { prev->gc.next = (EjsGCLink*) np->gc.next; } else { slab->allocList[0].next = (EjsGCLink*) np->gc.next; } break; } prev = np; } if (np == 0) { mprAssert(0); } }#endif /* * Insert into the free list. Only use the next ptr */ block = (EjsGCLink*) ptr;#if BLD_DEBUG#if !BREW || BREW_SIMULATOR if ((uint) block == breakAddr) { mprBreakpoint(MPR_LOC, "Watched Block"); }#endif mprAssert(block->magic == EJS_MAGIC); block->magic = EJS_MAGIC_FREE;#endif block->next = slab->freeList.next; slab->freeList.next = block;#if BLD_FEATURE_ALLOC_STATS slab->allocCount--; if (++slab->freeCount >= slab->peakFree) { slab->peakFree = slab->freeCount; } slab->totalReclaimed++; if (slabIndex != 2) { slabIndex = slabIndex; }#endif}/******************************************************************************//* * Mark an object as being in-use. Traverse all properties for referenced * objects and base classes. */static void markObjByVar(Ejs *ep, EjsVar *obj){ EjsProperty *pp; EjsVar *vp, *baseClass; mprAssert(ep); mprAssert(obj); obj->objectState->gcMarked = 1;#if BLD_DEBUG if (ep->gc.debugLevel >= 3) { int indent = min(ep->gc.gcIndent * 2, 32); mprLog(ep, 0, "%.*s %-24s %.*s 0x%08X", indent, " ", obj->propertyName, 32 - indent, "................................ ", (uint) obj->objectState); ep->gc.gcIndent++; } ep->gc.objectsInUse++;#endif /* * Traverse all referenced objects * OPT -- optimize by directly accessing the object links and not using * ejsGetFirst/NextProperty. Then just examine objects * OPT -- first property in global is global. Should optimize this. */ pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL); while (pp) { vp = ejsGetVarPtr(pp); if (vp->type == EJS_TYPE_OBJECT) { if (!vp->objectState->gcMarked) {#if FUTURE /* * OPT -- we can use the dirty bit on objects to avoid * visiting permanent objects that are clean. If so, don't * forget the else case below. */ obj = vp->objectState; if ((!obj->alive && !obj->permanent) || obj->dirty)#endif markObjByVar(ep, vp); } } else {#if BLD_DEBUG if (ep->gc.debugLevel >= 3) { int indent = min(ep->gc.gcIndent * 2, 32); mprLog(ep, 0, "%.*s %-24s %.*s %s", indent, " ", vp->propertyName, 32 - indent, "................................ ", ejsGetVarTypeAsString(vp)); } ep->gc.propertiesInUse++;#endif } pp = ejsGetNextProperty(pp, EJS_ENUM_ALL); } /* * Traverse the base class */ baseClass = obj->objectState->baseClass; if (baseClass) { mprAssert(baseClass->type == EJS_TYPE_OBJECT); mprAssert(baseClass->objectState); if (baseClass->objectState) { if (! baseClass->objectState->gcMarked) { markObjByVar(ep, baseClass); } } }#if BLD_DEBUG if (ep->gc.debugLevel >= 3) { ep->gc.gcIndent--; }#endif}/******************************************************************************//* * Mark phase. Examine all variable frames and the return result. */static void mark(Ejs *ep){ EjsVar *vp; int i;#if BLD_DEBUG if (ep->gc.debugLevel >= 3) { mprLog(ep, 0, " "); mprLog(ep, 0, "GC: Marked Blocks:"); }#endif if (ep->frames) { for (i = 0; i < mprGetItemCount(ep->frames); i++) { vp = (EjsVar*) mprGetItem(ep->frames, i); mprAssert(vp->type == EJS_TYPE_OBJECT); if (! vp->objectState->gcMarked) { markObjByVar(ep, vp); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -