diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2019-11-08 20:04:31 -0800 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2019-11-08 20:04:31 -0800 |
commit | 05243452efd861e872a6d5c612c23a2cdea86ec5 (patch) | |
tree | e28cc1881df53c94c7df3577b795c4e3e451f2b9 /gc.c | |
parent | e8a6f437306d6e6dc3576a47181aad3a2b741f2b (diff) | |
download | txr-05243452efd861e872a6d5c612c23a2cdea86ec5.tar.gz txr-05243452efd861e872a6d5c612c23a2cdea86ec5.tar.bz2 txr-05243452efd861e872a6d5c612c23a2cdea86ec5.zip |
gc: free heaps that become empty.
On glibc, our heap allocation requests are considered
large and handled via mmap; when we free a heap, the memory
is returned to the OS via munmap.
* gc.c (sweep): If every object in a heap is freed,
we free the entire heap, taking care to also reset the free
list to the state before those objects were added to it.
The free list may still contain objects from that same heap
that were not just added to it (they were freed in a previous
GC pass), so we must walk the free list to find the remaining
objects and remove them. The Valgrind debugging logic (opening
access and closing while walking the list) was too cumbersome
so it's done in two passes: open access to the whole free
list, process it, close off what is left.
Diffstat (limited to 'gc.c')
-rw-r--r-- | gc.c | 43 |
1 files changed, 41 insertions, 2 deletions
@@ -607,7 +607,7 @@ static int sweep_one(obj_t *block) static int_ptr_t sweep(void) { int_ptr_t free_count = 0; - heap_t *heap; + heap_t **pph; #if HAVE_VALGRIND const int vg_dbg = opt_vg_debug; #endif @@ -632,8 +632,11 @@ static int_ptr_t sweep(void) #endif - for (heap = heap_list; heap != 0; heap = heap->next) { + for (pph = &heap_list; *pph != 0; ) { obj_t *block, *end; + heap_t *heap = *pph; + int_ptr_t old_count = free_count; + val old_free_list = free_list; #if HAVE_VALGRIND if (vg_dbg) @@ -646,6 +649,42 @@ static int_ptr_t sweep(void) { free_count += sweep_one(block); } + + if (free_count - old_count == HEAP_SIZE) { + val *ppf; + + free_list = old_free_list; +#if HAVE_VALGRIND + if (vg_dbg) { + val iter; + for (iter = free_list; iter; iter = iter->t.next) + VALGRIND_MAKE_MEM_DEFINED(iter, sizeof *iter); + } +#endif + for (ppf = &free_list; *ppf != nil; ) { + val block = *ppf; + if (block >= heap->block && block < end) { + *ppf = block->t.next; + } else { + ppf = &block->t.next; + } + } + if (free_list == 0) + free_tail = &free_list; + *pph = heap->next; + free(heap); +#if HAVE_VALGRIND + if (vg_dbg) { + val iter, next; + for (iter = free_list; iter; iter = next) { + next = iter->t.next; + VALGRIND_MAKE_MEM_NOACCESS(iter, sizeof *iter); + } + } +#endif + } else { + pph = &(*pph)->next; + } } return free_count; |