3 * @brief EJS Garbage collector.
4 * @overview This implements a generational mark and sweep collection scheme.
6 /********************************* Copyright **********************************/
10 * Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
12 * This software is distributed under commercial and open source licenses.
13 * You may use the GPL open source license described below or you may acquire
14 * a commercial license from Mbedthis Software. You agree to be fully bound
15 * by the terms of either license. Consult the LICENSE.TXT distributed with
16 * this software for full details.
18 * This software is open source; you can redistribute it and/or modify it
19 * under the terms of the GNU General Public License as published by the
20 * Free Software Foundation; either version 2 of the License, or (at your
21 * option) any later version. See the GNU General Public License for more
22 * details at: http://www.mbedthis.com/downloads/gplLicense.html
24 * This program is distributed WITHOUT ANY WARRANTY; without even the
25 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
27 * This GPL license does NOT permit incorporating this software into
28 * proprietary programs. If you are unable to comply with the GPL, you must
29 * acquire a commercial license to use this software. Commercial licenses
30 * for this software and support services are available from Mbedthis
31 * Software at http://www.mbedthis.com
35 /********************************** Includes **********************************/
41 /****************************** Forward Declarations **************************/
43 static void mark(Ejs *ep);
44 static void markObjByVar(Ejs *ep, EjsVar *op);
45 static void markObj(EjsObj *obj);
46 static void markPerm(Ejs *ep, uint gen);
47 static int sweep(Ejs *ep, uint gen);
48 static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex);
49 static void ejsGracefulDegrade(Ejs *ep);
50 static void resetMarks(Ejs *ep, EjsSlab *slab);
53 static void ageGenerations(Ejs *ep);
56 #if BLD_DEBUG && (!BREW || BREW_SIMULATOR)
60 /************************************* Code ***********************************/
62 void ejsGCInit(Ejs *ep, int objInc, int propInc, int varInc, int strInc)
66 if (ep->service && ep->service->globalClass) {
67 ep->service->globalClass->objectState->gcMarked = 1;
70 slab = &ep->slabs[EJS_SLAB_OBJ];
71 slab->allocIncrement = objInc;
72 slab->size = EJS_ALLOC_ALIGN(sizeof(EjsObj));
74 slab = &ep->slabs[EJS_SLAB_PROPERTY];
75 slab->allocIncrement = propInc;
76 slab->size = EJS_ALLOC_ALIGN(sizeof(EjsProperty));
78 slab = &ep->slabs[EJS_SLAB_VAR];
79 slab->allocIncrement = varInc;
80 slab->size = EJS_ALLOC_ALIGN(sizeof(EjsVar));
84 * Enable GC both idle and demand collections.
85 * Set no limits and garbage collect if the slabs are
86 * empty and we have used more than the THRESHOLD of ram.
88 ep->gc.debugLevel = 0;
90 ep->gc.enableIdleCollect = 1;
91 ep->gc.enableDemandCollect = 1;
92 ep->gc.workQuota = EJS_GC_WORK_QUOTA;
97 /******************************************************************************/
98 #if BLD_FEATURE_ALLOC_STATS
100 void ejsPrintAllocReport(Ejs *ep, bool printLeakReport)
104 int slabIndex, isObj;
106 for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) {
107 slab = &ep->slabs[slabIndex];
108 if (slabIndex == EJS_SLAB_VAR) {
110 } else if (slabIndex == EJS_SLAB_PROPERTY) {
116 mprLog(ep, 0, " GC \"%s\" local slab", name);
117 mprLog(ep, 0, " Total blocks %14d",
118 slab->allocCount + slab->freeCount);
119 mprLog(ep, 0, " Block size %14d", slab->size);
120 mprLog(ep, 0, " Slab RAM allocated %14d",
121 (slab->allocCount + slab->freeCount) * slab->size);
122 mprLog(ep, 0, " Slab RAM in use %14d",
123 slab->allocCount * slab->size);
124 mprLog(ep, 0, " Blocks in use %14d", slab->allocCount);
125 mprLog(ep, 0, " Free blocks %14d", slab->freeCount);
126 mprLog(ep, 0, " Peak allocated %14d", slab->peakAllocated);
127 mprLog(ep, 0, " Peak free %14d", slab->peakFree);
128 mprLog(ep, 0, " Total allocations %14d", slab->totalAlloc);
129 mprLog(ep, 0, " Total blocks reclaimed %14d", slab->totalReclaimed);
130 mprLog(ep, 0, " Total sweeps %14d", slab->totalSweeps);
131 mprLog(ep, 0, " Allocation inc %14d", slab->allocIncrement);
135 mprLog(ep, 0, " Total EJS memory in use %10d", ejsGetUsedMemory(ep));
136 mprLog(ep, 0, " Total EJS memory allocated %10d",
137 ejsGetAllocatedMemory(ep));
139 if (printLeakReport) {
141 for (slabIndex = 0; slabIndex < EJS_SLAB_MAX; slabIndex++) {
144 slab = &ep->slabs[slabIndex];
148 if (slabIndex == EJS_SLAB_VAR) {
150 size = sizeof(EjsVar);
151 } else if (slabIndex == EJS_SLAB_PROPERTY) {
153 size = sizeof(EjsProperty);
156 size = sizeof(EjsObj);
159 #if BLD_FEATURE_ALLOC_LEAK_TRACK
165 mprLog(ep, 0, "EJS Leak Report for \"%s\"", name);
168 for (lp = slab->allocList[0].next; lp; lp = lp->next) {
169 mprLog(ep, 0, " %-20s %10d", lp->allocatedBy, size);
172 mprLog(ep, 0, " %-20s %10d %s %s",
173 lp->allocatedBy, size,
174 obj->permanent ? "permanent" : "",
175 obj->alive ? "alive" : ""
178 mprLog(ep, 0, " %-20s %10d", lp->allocatedBy,
183 mprLog(ep, 0, " Total blocks %14d", count);
192 /******************************************************************************/
197 static EjsGCLink *ejsAlloc(EJS_LOC_DEC(ep, loc), int slabIndex)
202 uint allocatedMemory;
207 if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) {
213 * See if the slab has some free blocks
215 slab = &ep->slabs[slabIndex];
216 if ((block = slab->freeList.next) == 0) {
218 allocatedMemory = ejsGetAllocatedMemory(ep);
222 * No blocks available. If demand collection is enabled, try
223 * to garbage collect first. We collect if we have done a good
224 * work quota or we are over the max memory limit.
226 if (slabIndex != EJS_SLAB_VAR &&
227 ep->gc.enable && ep->gc.enableDemandCollect) {
228 if ((ep->gc.workDone > ep->gc.workQuota) ||
229 (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory)) {
232 if (ep->gc.debugLevel > 0) {
233 mprLog(ep, 0, "Need GC, EJS RAM %d, MPR RAM %d\n",
234 allocatedMemory, mprGetAllocatedMemory(ep));
235 if (ep->gc.debugLevel > 4) {
236 ejsPrintAllocReport(ep, 0);
240 if (ejsCollectGarbage(ep, slabIndex) == 0) {
241 block = slab->freeList.next;
247 if (gc->maxMemory > 0 && allocatedMemory > gc->maxMemory) {
249 * We are above the max memory limit. We will fail this
250 * memory allocation, but allow subsequent allocations to
251 * permit error recovery. We gracefully degrade by setting
252 * slab chunk sizes to 1. This minimizes real memory
253 * consumption. This allows us to create
254 * an exception block to be created by upper layers.
256 if (! gc->degraded) {
257 ejsGracefulDegrade(ep);
263 * Still non available, so allocate more memory for a set of blocks
264 * OPT -- should bypass mprAlloc. Need mprMalloc.
266 block = mprAlloc(ep->slabAllocContext,
267 slab->size * slab->allocIncrement);
270 * Now we're in trouble. We should really never get here
271 * as the graceful degrade will have signaled a memory
272 * allocation failure.
274 mprAssert(block != 0);
279 * Chain all the blocks together onto the slab free list
281 for (i = slab->allocIncrement - 1; i >= 0; i--) {
282 block->next = slab->freeList.next;
284 block->magic = EJS_MAGIC_FREE;
286 slab->freeList.next = block;
287 block = (EjsGCLink*) ((char*) block + slab->size);
290 block = slab->freeList.next;
292 #if BLD_FEATURE_ALLOC_STATS
293 slab->freeCount += slab->allocIncrement;
294 if (slab->freeCount > slab->peakFree) {
295 slab->peakFree = slab->freeCount;
302 * We use block to point to the user data in the block. We only
303 * store the magic number (if debug). No other data is stored in the
307 mprAssert(block->magic == EJS_MAGIC_FREE);
311 * Remove from the free list
313 slab->freeList.next = block->next;
318 memset(block, 0, slab->size);
321 block->magic = EJS_MAGIC;
324 #if BLD_FEATURE_ALLOC_STATS
326 if (++slab->allocCount > slab->peakAllocated) {
327 slab->peakAllocated = slab->allocCount;
332 #if BLD_DEBUG && (!BREW || BREW_SIMULATOR)
333 if ((uint) block == breakAddr) {
334 mprBreakpoint(MPR_LOC, "Watched Block");
341 /******************************************************************************/
343 EjsObj *ejsAllocObj(EJS_LOC_DEC(ep, loc))
348 obj = (EjsObj*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_OBJ);
351 * Add to the allocated block list for the New generation.
354 slab = &ep->slabs[EJS_SLAB_OBJ];
355 obj->gc.next = slab->allocList[EJS_GEN_NEW].next;
357 #if BLD_FEATURE_ALLOC_LEAK_TRACK
358 obj->gc.allocatedBy = loc;
362 slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) obj;
371 /******************************************************************************/
373 EjsProperty *ejsAllocProperty(EJS_LOC_DEC(ep, loc))
377 prop = (EjsProperty*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_PROPERTY);
381 prop->var.type = EJS_TYPE_NULL;
382 prop->var.isProperty = 1;
383 #if BLD_FEATURE_ALLOC_LEAK_TRACK
384 prop->var.gc.allocatedBy = loc;
391 /******************************************************************************/
393 EjsVar *ejsAllocVar(EJS_LOC_DEC(ep, loc))
397 vp = (EjsVar*) ejsAlloc(EJS_LOC_PASS(ep, loc), EJS_SLAB_VAR);
401 #if BLD_FEATURE_ALLOC_LEAK_TRACK
403 vp->gc.allocatedBy = loc;
404 slab = &ep->slabs[EJS_SLAB_VAR];
405 vp->gc.next = slab->allocList[EJS_GEN_NEW].next;
406 slab->allocList[EJS_GEN_NEW].next = (EjsGCLink*) vp;
409 vp->propertyName = 0;
416 /******************************************************************************/
418 * Return the block back to the relevant slab
421 void ejsFree(Ejs *ep, void *ptr, int slabIndex)
429 if (slabIndex < 0 || slabIndex >= EJS_SLAB_MAX) {
430 mprAssert(slabIndex >= 0 && slabIndex < EJS_SLAB_MAX);
433 slab = &ep->slabs[slabIndex];
435 #if BLD_FEATURE_ALLOC_LEAK_TRACK
436 if (slabIndex == EJS_SLAB_VAR) {
437 EjsVar *vp, *np, *prev;
440 * Remove the block rom the alloc list. WARNING: this is slow
441 * and should not be used in production code.
445 for (np = (EjsVar*) slab->allocList[0].next; np;
446 np = (EjsVar*) np->gc.next) {
449 prev->gc.next = (EjsGCLink*) np->gc.next;
451 slab->allocList[0].next = (EjsGCLink*) np->gc.next;
464 * Insert into the free list. Only use the next ptr
466 block = (EjsGCLink*) ptr;
469 #if !BREW || BREW_SIMULATOR
470 if ((uint) block == breakAddr) {
471 mprBreakpoint(MPR_LOC, "Watched Block");
474 mprAssert(block->magic == EJS_MAGIC);
475 block->magic = EJS_MAGIC_FREE;
478 block->next = slab->freeList.next;
479 slab->freeList.next = block;
481 #if BLD_FEATURE_ALLOC_STATS
483 if (++slab->freeCount >= slab->peakFree) {
484 slab->peakFree = slab->freeCount;
486 slab->totalReclaimed++;
487 if (slabIndex != 2) {
488 slabIndex = slabIndex;
493 /******************************************************************************/
495 * Mark an object as being in-use. Traverse all properties for referenced
496 * objects and base classes.
499 static void markObjByVar(Ejs *ep, EjsVar *obj)
502 EjsVar *vp, *baseClass;
507 obj->objectState->gcMarked = 1;
510 if (ep->gc.debugLevel >= 3) {
511 int indent = min(ep->gc.gcIndent * 2, 32);
512 mprLog(ep, 0, "%.*s %-24s %.*s 0x%08X",
515 32 - indent, "................................ ",
516 (uint) obj->objectState);
519 ep->gc.objectsInUse++;
523 * Traverse all referenced objects
524 * OPT -- optimize by directly accessing the object links and not using
525 * ejsGetFirst/NextProperty. Then just examine objects
526 * OPT -- first property in global is global. Should optimize this.
528 pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
530 vp = ejsGetVarPtr(pp);
531 if (vp->type == EJS_TYPE_OBJECT) {
532 if (!vp->objectState->gcMarked) {
535 * OPT -- we can use the dirty bit on objects to avoid
536 * visiting permanent objects that are clean. If so, don't
537 * forget the else case below.
539 obj = vp->objectState;
540 if ((!obj->alive && !obj->permanent) || obj->dirty)
542 markObjByVar(ep, vp);
547 if (ep->gc.debugLevel >= 3) {
548 int indent = min(ep->gc.gcIndent * 2, 32);
549 mprLog(ep, 0, "%.*s %-24s %.*s %s",
552 32 - indent, "................................ ",
553 ejsGetVarTypeAsString(vp));
555 ep->gc.propertiesInUse++;
558 pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
562 * Traverse the base class
564 baseClass = obj->objectState->baseClass;
566 mprAssert(baseClass->type == EJS_TYPE_OBJECT);
567 mprAssert(baseClass->objectState);
568 if (baseClass->objectState) {
569 if (! baseClass->objectState->gcMarked) {
570 markObjByVar(ep, baseClass);
575 if (ep->gc.debugLevel >= 3) {
582 /******************************************************************************/
584 * Mark phase. Examine all variable frames and the return result.
587 static void mark(Ejs *ep)
593 if (ep->gc.debugLevel >= 3) {
595 mprLog(ep, 0, "GC: Marked Blocks:");
600 for (i = 0; i < mprGetItemCount(ep->frames); i++) {
602 vp = (EjsVar*) mprGetItem(ep->frames, i);
603 mprAssert(vp->type == EJS_TYPE_OBJECT);
605 if (! vp->objectState->gcMarked) {
606 markObjByVar(ep, vp);
612 if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) {
613 markObjByVar(ep, vp);
617 if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) {
618 markObjByVar(ep, vp);
621 vp = ejsGetVarPtr(ep->currentProperty);
622 if (vp && vp->type == EJS_TYPE_OBJECT && ! vp->objectState->gcMarked) {
623 markObjByVar(ep, vp);
627 * OPT -- we could mark master as "mark permanent" somehow and
628 * then we would not need to walk the master objects.
630 if (ep->slabAllocContext == ep->service->master) {
631 if (ep->service->master->global) {
632 markObjByVar(ep, ep->service->master->global);
637 if (ep->gc.debugLevel >= 3) {
644 /******************************************************************************/
647 static void resetMark(EjsVar *obj)
650 EjsVar *vp, *baseClass;
652 obj->objectState->gcMarked = 0;
653 obj->objectState->visited = 1;
655 pp = ejsGetFirstProperty(obj, EJS_ENUM_ALL);
657 vp = ejsGetVarPtr(pp);
658 if (vp->type == EJS_TYPE_OBJECT && !vp->objectState->visited) {
661 pp = ejsGetNextProperty(pp, EJS_ENUM_ALL);
664 baseClass = obj->objectState->baseClass;
666 mprAssert(baseClass->type == EJS_TYPE_OBJECT);
667 mprAssert(baseClass->objectState);
668 if (baseClass->objectState) {
669 if (! baseClass->objectState->visited) {
670 resetMark(baseClass);
674 obj->objectState->visited = 0;
677 /******************************************************************************/
679 * Mark phase. Examine all variable frames and the return result.
682 static void resetAllMarks(Ejs *ep)
687 for (i = 0; i < mprGetItemCount(ep->frames); i++) {
688 vp = (EjsVar*) mprGetItem(ep->frames, i);
692 if (ep->result && ep->result->type == EJS_TYPE_OBJECT &&
693 ! ep->result->objectState->gcMarked) {
694 resetMark(ep->result);
699 /******************************************************************************/
701 * Sweep up the garbage
704 static void resetMarks(Ejs *ep, EjsSlab *slab)
710 for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) {
711 obj = (EjsObj*) slab->allocList[gen].next;
712 for (; obj; obj = (EjsObj*) obj->gc.next) {
719 for (i = 0; i < mprGetItemCount(ep->frames); i++) {
721 vp = (EjsVar*) mprGetItem(ep->frames, i);
722 mprAssert(vp->type == EJS_TYPE_OBJECT);
724 vp->objectState->gcMarked = 0;
725 vp->objectState->visited = 0;
729 if (ep->result && ep->result->type == EJS_TYPE_OBJECT) {
730 ep->result->objectState->gcMarked = 0;
734 /******************************************************************************/
736 * Mark all permanent and non-alive objects
739 static void markPerm(Ejs *ep, uint gen)
744 slab = &ep->slabs[EJS_SLAB_OBJ];
746 for (obj = (EjsObj*) slab->allocList[gen].next; obj; ) {
748 if (! obj->gcMarked) {
749 if (!obj->alive || obj->permanent) {
753 obj = (EjsObj*) obj->gc.next;
758 /******************************************************************************/
760 static void markObj(EjsObj *obj)
763 EjsPropLink *lp, *head;
771 for (lp = head->next; lp != head; lp = lp->next) {
773 pp = ejsGetPropertyFromLink(lp);
775 if (pp->var.type == EJS_TYPE_OBJECT) {
776 op = pp->var.objectState;
777 if (op != 0 && !op->gcMarked) {
784 /******************************************************************************/
786 * Sweep up the garbage. Return the number of objects freed.
789 static int sweep(Ejs *ep, uint gen)
792 EjsObj *obj, *next, *prev;
795 slab = &ep->slabs[EJS_SLAB_OBJ];
798 * Examine allocated objects in the specified generation (only).
799 * NOTE: we only sweep object allocated to this interpreter and so
800 * we do not sweep any permanent objects in the default interpreter.
804 for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) {
806 next = (EjsObj*) obj->gc.next;
808 #if BLD_DEBUG && (!BREW || BREW_SIMULATOR)
809 if ((uint) obj == breakAddr) {
810 mprBreakpoint(MPR_LOC, "Watched Block");
815 * If object has not been marked inuse and is not a permanent
816 * object, then free it.
818 if (! obj->gcMarked && obj->alive && !obj->permanent) {
821 if (ep->gc.debugLevel >= 2) {
823 mprLog(ep, 0, "GC: destroy %-18s %10d, %8X",
824 obj->objName, (uint) obj, (uint) obj);
826 mprLog(ep, 0, "GC: destroy UNKNOWN %x", (uint) obj);
830 if (ejsDestroyObj(ep, obj) < 0) {
837 prev->gc.next = (EjsGCLink*) next;
839 slab->allocList[gen].next = (EjsGCLink*) next;
845 /* Reset for next time */
850 if (gen == (EJS_GEN_OLD - 1)) {
851 slab->lastRecentBlock = prev;
853 #if BLD_FEATURE_ALLOC_STATS
857 if (ep->gc.debugLevel > 0) {
858 mprLog(ep, 0, "GC: Sweep freed %d objects", count);
864 /******************************************************************************/
866 * Sweep all variables
869 void ejsSweepAll(Ejs *ep)
872 EjsObj *obj, *next, *prev;
875 slab = &ep->slabs[EJS_SLAB_OBJ];
877 for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) {
879 for (obj = (EjsObj*) slab->allocList[gen].next; obj; obj = next) {
880 next = (EjsObj*) obj->gc.next;
881 ejsDestroyObj(ep, obj);
887 /******************************************************************************/
889 bool ejsObjIsCollectable(EjsVar *vp)
891 if (vp == 0 || !ejsVarIsObject(vp)) {
894 return (vp->objectState->alive && !vp->objectState->permanent);
897 /******************************************************************************/
900 static void ageGenerations(Ejs *ep)
906 slab = &ep->slabs[EJS_SLAB_OBJ];
909 * Age all blocks. First append all (old - 1) blocks onto the old
912 oldList = &slab->allocList[EJS_GEN_OLD];
914 if (slab->lastRecentBlock) {
915 slab->lastRecentBlock->gc.next = oldList->next;
916 oldList->next = (EjsGCLink*) slab->lastRecentBlock;
920 * Now simply copy all allocation lists up one generation
922 for (gen = EJS_GEN_OLD - 1; gen > 0; gen--) {
923 slab->allocList[gen] = slab->allocList[gen - 1];
925 slab->allocList[0].next = 0;
929 /******************************************************************************/
931 * Collect the garbage. This is a mark and sweep over all possible objects.
932 * If an object is not referenced, it and all contained properties will be
933 * freed. If a slabIndex is provided, the collection halts when a block is
934 * available for allocation on that slab.
936 * Return 0 if memory is now available after collecting garbage. Otherwise,
937 * return MPR_ERR_MEMORY.
940 int ejsCollectGarbage(Ejs *ep, int slabIndex)
944 if (ep->flags & EJS_FLAGS_DONT_GC) {
949 * Prevent destructors invoking the garbage collector
951 if (ep->gc.collecting) {
954 ep->gc.collecting = 1;
956 resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]);
959 * Examine each generation of objects starting with the most recent
960 * generation. Stop scanning when we have a free block to use.
962 for (gen = EJS_GEN_NEW; gen < EJS_GEN_MAX; gen++) {
964 if (slabIndex >= 0 && ep->slabs[slabIndex].freeList.next) {
969 * FUTURE OPT. Should mark objects in new generation and those
970 * with a dirty bit set in older generations. Don't need to mark
971 * entire heap. But how to keep list of dirty objects.
977 /* FUTURE - not using generations yet */
982 * FUTURE -- not using generations yet.
984 * ageGenerations(ep);
988 ep->gc.collecting = 0;
990 return (gen < EJS_GEN_MAX) ? 0 : MPR_ERR_MEMORY;
994 /******************************************************************************/
996 * Should be called when the app has been idle for a little while and when it
997 * is likely to be idle a bit longer. Call ejsIsTimeForGC to see if this is
998 * true. Return the count of objects collected .
1001 int ejsIncrementalCollectGarbage(Ejs *ep)
1005 if (ep->gc.collecting) {
1009 ep->gc.collecting = 1;
1011 resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]);
1014 /* Not generational yet */
1015 count = sweep(ep, EJS_GEN_NEW);
1017 ep->gc.collecting = 0;
1018 ep->gc.workDone = 0;
1023 /******************************************************************************/
1026 void ejsDumpObjects(Ejs *ep)
1030 mprLog(ep, 0, "Dump of objects in use\n");
1032 oldDebugLevel = ep->gc.debugLevel;
1034 ep->gc.debugLevel = 3;
1035 ep->gc.objectsInUse = 0;
1036 ep->gc.propertiesInUse = 0;
1037 ep->gc.collecting = 1;
1039 resetMarks(ep, &ep->slabs[EJS_SLAB_OBJ]);
1042 ep->gc.collecting = 0;
1043 ep->gc.debugLevel = oldDebugLevel;
1045 mprLog(ep, 0, "%d objects and %d properties in use",
1046 ep->gc.objectsInUse, ep->gc.propertiesInUse);
1047 mprLog(ep, 0, "%d object bytes, %d property bytes and %d total",
1048 (int) (ep->gc.objectsInUse * sizeof(EjsObj)),
1049 (int) (ep->gc.propertiesInUse * sizeof(EjsProperty)),
1050 (int) ((ep->gc.objectsInUse * sizeof(EjsObj) +
1051 ep->gc.propertiesInUse * sizeof(EjsProperty))));
1055 /******************************************************************************/
1057 * Return true if there is time to do a garbage collection and if we will
1061 int ejsIsTimeForGC(Ejs *ep, int timeTillNextEvent)
1065 if (timeTillNextEvent < EJS_MIN_TIME_FOR_GC) {
1067 * Not enough time to complete a collection
1075 * Return if we haven't done enough work to warrant a collection
1076 * Trigger a little short of the work quota to try to run GC before
1077 * a demand allocation requires it.
1079 if (!gc->enable || !gc->enableIdleCollect ||
1080 (gc->workDone < (gc->workQuota - EJS_GC_MIN_WORK_QUOTA))) {
1085 mprLog(ep, 0, "Time for GC. Work done %d, time till next event %d",
1086 gc->workDone, timeTillNextEvent);
1091 /******************************************************************************/
1093 * Return the amount of memory in use by EJS
1096 uint ejsGetUsedMemory(Ejs *ep)
1098 #if BLD_FEATURE_ALLOC_STATS
1100 int i, totalMemory, slabMemory;
1103 for (i = 0; i < EJS_SLAB_MAX; i++) {
1104 slab = &ep->slabs[i];
1105 slabMemory = slab->allocCount * slab->size;
1106 totalMemory += slabMemory;
1114 /******************************************************************************/
1116 * Return the amount of memory allocated by EJS
1119 uint ejsGetAllocatedMemory(Ejs *ep)
1121 #if BLD_FEATURE_ALLOC_STATS
1123 int i, totalMemory, slabMemory;
1126 for (i = 0; i < EJS_SLAB_MAX; i++) {
1127 slab = &ep->slabs[i];
1128 slabMemory = (slab->allocCount + slab->freeCount) * slab->size;
1129 totalMemory += slabMemory;
1137 /******************************************************************************/
1139 * On a memory allocation failure, go into graceful degrade mode. Set all
1140 * slab allocation chunk increments to 1 so we can create an exception block
1144 static void ejsGracefulDegrade(Ejs *ep)
1149 mprLog(ep, 1, "WARNING: Memory almost depleted. In graceful degrade mode");
1150 for (i = 0; i < EJS_SLAB_MAX; i++) {
1151 slab = &ep->slabs[i];
1152 slab->allocIncrement = 8;
1154 ep->gc.degraded = 1;
1157 /******************************************************************************/
1159 int ejsSetGCDebugLevel(Ejs *ep, int debugLevel)
1163 old = ep->gc.debugLevel;
1164 ep->gc.debugLevel = debugLevel;
1168 /******************************************************************************/
1170 int ejsSetGCMaxMemory(Ejs *ep, uint maxMemory)
1174 old = ep->gc.maxMemory;
1175 ep->gc.maxMemory = maxMemory;
1180 /******************************************************************************/
1182 bool ejsBlockInUseInt(EjsVar *vp)
1186 if (vp->gc.magic != EJS_MAGIC) {
1189 if (vp->type == EJS_TYPE_OBJECT && vp->objectState &&
1190 vp->objectState->gc.magic != EJS_MAGIC) {
1199 /******************************************************************************/
1201 void ejsGarbageDummy() {}
1203 #endif /* BLD_FEATURE_EJS */
1205 /******************************************************************************/
1212 * vim600: sw=4 ts=4 fdm=marker
1213 * vim<600: sw=4 ts=4