Repository: JuliaLang/julia
File: src/gc.c (C)

Code before:
                jl_gc_pool_t *p = &ptls2->heap.norm_pools[i];
                p->newpages = NULL;
            }
        }
    }
    free(tmp);

    // merge free lists
    for (int t_i = 0; t_i < n_threads; t_i++) {
        jl_ptls_t ptls2 = gc_all_tls_states[t_i];
        if (ptls2 == NULL) {
            continue;
        }
        jl_gc_pagemeta_t *pg = jl_atomic_load_relaxed(&ptls2->page_metadata_allocd.bottom);
        while (pg != NULL) {
            jl_gc_pagemeta_t *pg2 = pg->next;
            if (pg->fl_begin_offset != UINT16_MAX) {
                char *cur_pg = pg->data;
                jl_taggedvalue_t *fl_beg = (jl_taggedvalue_t*)(cur_pg + pg->fl_begin_offset);
                jl_taggedvalue_t *fl_end = (jl_taggedvalue_t*)(cur_pg + pg->fl_end_offset);
                *pfl[t_i * JL_GC_N_POOLS + pg->pool_n] = fl_beg;
                pfl[t_i * JL_GC_N_POOLS + pg->pool_n] = &fl_end->next;
            }
            pg = pg2;
        }
    }

    // null out terminal pointers of free lists
    for (int t_i = 0; t_i < n_threads; t_i++) {
        jl_ptls_t ptls2 = gc_all_tls_states[t_i];
        if (ptls2 != NULL) {
            for (int i = 0; i < JL_GC_N_POOLS; i++) {
                *pfl[t_i * JL_GC_N_POOLS + i] = NULL;
            }
        }
    }
    free(pfl);

#ifdef _P64 // only enable concurrent sweeping on 64bit
    // wake thread up to sweep concurrently
    if (jl_n_sweepthreads > 0) {
        uv_sem_post(&gc_sweep_assists_needed);
    }
    else {
        gc_free_pages();
    }
#else
    gc_free_pages();
#endif
    gc_dump_page_utilization_data();
    gc_time_pool_end(current_sweep_full);

Review comment (suggestion):
We likely need to subtract the memory we freed from `gc_heap_stats.heap_size`, as we do [here](https://github.com/JuliaLang/julia/blob/a6c656e6c47ff2b1237c92e90ba73ac267fc1dc0/src/gc.c#L1556C11-L1556C11).

Code after:
// free all pages (i.e. through `madvise` on Linux) that were lazily freed
void gc_free_pages(void)
{
    while (1) {
        jl_gc_pagemeta_t *pg = pop_lf_back(&global_page_pool_lazily_freed);
        if (pg == NULL) {
            break;
        }
        jl_gc_free_page(pg);
        push_lf_back(&global_page_pool_freed, pg);
    }
}

void gc_move_to_global_page_pool(jl_gc_page_stack_t *pgstack)
{
    while (1) {
        jl_gc_pagemeta_t *pg = pop_lf_back(pgstack);
        if (pg == NULL) {
            break;
        }
        jl_gc_free_page(pg);
        jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -GC_PAGE_SZ);
        push_lf_back(&global_page_pool_freed, pg);
    }
}

// setup the data-structures for a sweep over all memory pools
static void gc_sweep_pool(void)
{
    gc_time_pool_start();
    buffered_pages = 0;

    // For the benefit of the analyzer, which doesn't know that gc_n_threads
    // doesn't change over the course of this function
    size_t n_threads = gc_n_threads;

    // allocate enough space to hold the end of the free list chain
    // for every thread and pool size
    jl_taggedvalue_t ***pfl = (jl_taggedvalue_t ***) malloc_s(n_threads * JL_GC_N_POOLS * sizeof(jl_taggedvalue_t**));

    // update metadata of pages that were pointed to by freelist or newpages from a pool
    // i.e. pages being the current allocation target
    for (int t_i = 0; t_i < n_threads; t_i++) {
        jl_ptls_t ptls2 = gc_all_tls_states[t_i];
        if (ptls2 == NULL) {
            for (int i = 0; i < JL_GC_N_POOLS; i++) {
                pfl[t_i * JL_GC_N_POOLS + i] = NULL;
            }
            continue;
        }
        jl_atomic_store_relaxed(&ptls2->gc_num.pool_live_bytes, 0);