DUK_LOCAL void duk__sweep_stringtable(duk_heap *heap, duk_size_t *out_count_keep) { duk_hstring *h; duk_hstring *prev; duk_uint32_t i; #if defined(DUK_USE_DEBUG) duk_size_t count_free = 0; #endif duk_size_t count_keep = 0; DUK_DD(DUK_DDPRINT("duk__sweep_stringtable: %p", (void *) heap)); #if defined(DUK_USE_STRTAB_PTRCOMP) if (heap->strtable16 == NULL) { #else if (heap->strtable == NULL) { #endif goto done; } for (i = 0; i < heap->st_size; i++) { #if defined(DUK_USE_STRTAB_PTRCOMP) h = DUK_USE_HEAPPTR_DEC16(heap->heap_udata, heap->strtable16[i]); #else h = heap->strtable[i]; #endif prev = NULL; while (h != NULL) { duk_hstring *next; next = h->hdr.h_next; if (DUK_HEAPHDR_HAS_REACHABLE((duk_heaphdr *) h)) { DUK_HEAPHDR_CLEAR_REACHABLE((duk_heaphdr *) h); count_keep++; prev = h; } else { #if defined(DUK_USE_DEBUG) count_free++; #endif #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen for unreachable strings, * because we refcount finalize all unreachable objects which * should have decreased unreachable string refcounts to zero * (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h) == 0); #endif /* deal with weak references first */ duk_heap_strcache_string_remove(heap, (duk_hstring *) h); /* remove the string from the string table */ duk_heap_strtable_unlink_prev(heap, (duk_hstring *) h, (duk_hstring *) prev); /* free inner references (these exist e.g. when external * strings are enabled) and the struct itself. */ duk_free_hstring(heap, (duk_hstring *) h); /* don't update 'prev'; it should be last string kept */ } h = next; } } done: #if defined(DUK_USE_DEBUG) DUK_D(DUK_DPRINT("mark-and-sweep sweep stringtable: %ld freed, %ld kept", (long) count_free, (long) count_keep)); #endif *out_count_keep = count_keep; } /* * Sweep heap */ DUK_LOCAL void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_count_keep) { duk_heaphdr *prev; /* last element that was left in the heap */ duk_heaphdr *curr; duk_heaphdr *next; #if defined(DUK_USE_DEBUG) duk_size_t count_free = 0; duk_size_t count_finalize = 0; duk_size_t count_rescue = 0; #endif duk_size_t count_keep = 0; DUK_UNREF(flags); DUK_DD(DUK_DDPRINT("duk__sweep_heap: %p", (void *) heap)); prev = NULL; curr = heap->heap_allocated; heap->heap_allocated = NULL; while (curr) { /* Strings and ROM objects are never placed on the heap allocated list. */ DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_STRING); DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY(curr)); next = DUK_HEAPHDR_GET_NEXT(heap, curr); if (DUK_HEAPHDR_HAS_REACHABLE(curr)) { /* * Reachable object, keep */ DUK_DDD(DUK_DDDPRINT("sweep, reachable: %p", (void *) curr)); if (DUK_HEAPHDR_HAS_FINALIZABLE(curr)) { /* * If object has been marked finalizable, move it to the * "to be finalized" work list. It will be collected on * the next mark-and-sweep if it is still unreachable * after running the finalizer. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); DUK_DDD(DUK_DDDPRINT("object has finalizer, move to finalization work list: %p", (void *) curr)); #if defined(DUK_USE_DOUBLE_LINKED_HEAP) if (heap->finalize_list) { DUK_HEAPHDR_SET_PREV(heap, heap->finalize_list, curr); } DUK_HEAPHDR_SET_PREV(heap, curr, NULL); #endif DUK_HEAPHDR_SET_NEXT(heap, curr, heap->finalize_list); DUK_ASSERT_HEAPHDR_LINKS(heap, curr); heap->finalize_list = curr; #if defined(DUK_USE_DEBUG) count_finalize++; #endif } else { /* * Object will be kept; queue object back to heap_allocated (to tail) */ if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { /* * Object's finalizer was executed on last round, and * object has been happily rescued. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); DUK_DD(DUK_DDPRINT("object rescued during mark-and-sweep finalization: %p", (void *) curr)); #if defined(DUK_USE_DEBUG) count_rescue++; #endif } else { /* * Plain, boring reachable object. */ DUK_DD(DUK_DDPRINT("keep object: %!iO", curr)); count_keep++; } if (!heap->heap_allocated) { heap->heap_allocated = curr; } if (prev) { DUK_HEAPHDR_SET_NEXT(heap, prev, curr); } #if defined(DUK_USE_DOUBLE_LINKED_HEAP) DUK_HEAPHDR_SET_PREV(heap, curr, prev); #endif DUK_ASSERT_HEAPHDR_LINKS(heap, prev); DUK_ASSERT_HEAPHDR_LINKS(heap, curr); prev = curr; } DUK_HEAPHDR_CLEAR_REACHABLE(curr); DUK_HEAPHDR_CLEAR_FINALIZED(curr); DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); curr = next; } else { /* * Unreachable object, free */ DUK_DDD(DUK_DDDPRINT("sweep, not reachable: %p", (void *) curr)); #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen because we refcount * finalize all unreachable objects which should cancel out * refcounts (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) == 0); #endif DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { DUK_DDD(DUK_DDDPRINT("finalized object not rescued: %p", (void *) curr)); } /* Note: object cannot be a finalizable unreachable object, as * they have been marked temporarily reachable for this round, * and are handled above. */ #if defined(DUK_USE_DEBUG) count_free++; #endif /* weak refs should be handled here, but no weak refs for * any non-string objects exist right now. */ /* free object and all auxiliary (non-heap) allocs */ duk_heap_free_heaphdr_raw(heap, curr); curr = next; } } if (prev) { DUK_HEAPHDR_SET_NEXT(heap, prev, NULL); } DUK_ASSERT_HEAPHDR_LINKS(heap, prev); #if defined(DUK_USE_DEBUG) DUK_D(DUK_DPRINT("mark-and-sweep sweep objects (non-string): %ld freed, %ld kept, %ld rescued, %ld queued for finalization", (long) count_free, (long) count_keep, (long) count_rescue, (long) count_finalize)); #endif *out_count_keep = count_keep; }
static void duk__sweep_heap(duk_heap *heap, duk_int_t flags, duk_size_t *out_count_keep) { duk_heaphdr *prev; /* last element that was left in the heap */ duk_heaphdr *curr; duk_heaphdr *next; #ifdef DUK_USE_DEBUG duk_size_t count_free = 0; duk_size_t count_finalize = 0; duk_size_t count_rescue = 0; #endif duk_size_t count_keep = 0; DUK_UNREF(flags); DUK_DD(DUK_DDPRINT("duk__sweep_heap: %p", (void *) heap)); prev = NULL; curr = heap->heap_allocated; heap->heap_allocated = NULL; while (curr) { /* strings are never placed on the heap allocated list */ DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) != DUK_HTYPE_STRING); next = DUK_HEAPHDR_GET_NEXT(curr); if (DUK_HEAPHDR_HAS_REACHABLE(curr)) { /* * Reachable object, keep */ DUK_DDD(DUK_DDDPRINT("sweep, reachable: %p", (void *) curr)); if (DUK_HEAPHDR_HAS_FINALIZABLE(curr)) { /* * If object has been marked finalizable, move it to the * "to be finalized" work list. It will be collected on * the next mark-and-sweep if it is still unreachable * after running the finalizer. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); DUK_DDD(DUK_DDDPRINT("object has finalizer, move to finalization work list: %p", (void *) curr)); #ifdef DUK_USE_DOUBLE_LINKED_HEAP if (heap->finalize_list) { DUK_HEAPHDR_SET_PREV(heap->finalize_list, curr); } DUK_HEAPHDR_SET_PREV(curr, NULL); #endif DUK_HEAPHDR_SET_NEXT(curr, heap->finalize_list); heap->finalize_list = curr; #ifdef DUK_USE_DEBUG count_finalize++; #endif } else { /* * Object will be kept; queue object back to heap_allocated (to tail) */ if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { /* * Object's finalizer was executed on last round, and * object has been happily rescued. */ DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(curr) == DUK_HTYPE_OBJECT); DUK_DD(DUK_DDPRINT("object rescued during mark-and-sweep finalization: %p", (void *) curr)); #ifdef DUK_USE_DEBUG count_rescue++; #endif } else { /* * Plain, boring reachable object. */ count_keep++; } if (!heap->heap_allocated) { heap->heap_allocated = curr; } if (prev) { DUK_HEAPHDR_SET_NEXT(prev, curr); } #ifdef DUK_USE_DOUBLE_LINKED_HEAP DUK_HEAPHDR_SET_PREV(curr, prev); #endif prev = curr; } DUK_HEAPHDR_CLEAR_REACHABLE(curr); DUK_HEAPHDR_CLEAR_FINALIZED(curr); DUK_HEAPHDR_CLEAR_FINALIZABLE(curr); DUK_ASSERT(!DUK_HEAPHDR_HAS_REACHABLE(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZED(curr)); DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); curr = next; } else { /* * Unreachable object, free */ DUK_DDD(DUK_DDDPRINT("sweep, not reachable: %p", (void *) curr)); #if defined(DUK_USE_REFERENCE_COUNTING) /* Non-zero refcounts should not happen because we refcount * finalize all unreachable objects which should cancel out * refcounts (even for cycles). */ DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(curr) == 0); #endif DUK_ASSERT(!DUK_HEAPHDR_HAS_FINALIZABLE(curr)); if (DUK_HEAPHDR_HAS_FINALIZED(curr)) { DUK_DDD(DUK_DDDPRINT("finalized object not rescued: %p", (void *) curr)); } /* Note: object cannot be a finalizable unreachable object, as * they have been marked temporarily reachable for this round, * and are handled above. */ #ifdef DUK_USE_DEBUG count_free++; #endif /* weak refs should be handled here, but no weak refs for * any non-string objects exist right now. */ /* free object and all auxiliary (non-heap) allocs */ duk_heap_free_heaphdr_raw(heap, curr); curr = next; } } if (prev) { DUK_HEAPHDR_SET_NEXT(prev, NULL); } #ifdef DUK_USE_DEBUG DUK_D(DUK_DPRINT("mark-and-sweep sweep objects (non-string): %d freed, %d kept, %d rescued, %d queued for finalization", (int) count_free, (int) count_keep, (int) count_rescue, (int) count_finalize)); #endif *out_count_keep = count_keep; }