summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2019-11-08 20:04:31 -0800
committerKaz Kylheku <kaz@kylheku.com>2019-11-08 20:04:31 -0800
commite9bf49f0e7b1fe2160c5f965cd51049edc0b5e35 (patch)
treee28cc1881df53c94c7df3577b795c4e3e451f2b9
parentdaf73eb3f023a3845f6609be016f527a0ab7763c (diff)
downloadtxr-e9bf49f0e7b1fe2160c5f965cd51049edc0b5e35.tar.gz
txr-e9bf49f0e7b1fe2160c5f965cd51049edc0b5e35.tar.bz2
txr-e9bf49f0e7b1fe2160c5f965cd51049edc0b5e35.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.
-rw-r--r--gc.c43
1 files changed, 41 insertions, 2 deletions
diff --git a/gc.c b/gc.c
index 93a91267..8e84af01 100644
--- a/gc.c
+++ b/gc.c
@@ -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;