Skip to content

Commit 9839aa3

Browse files
authored
free more thread state in jl_delete_thread and GC (#52198)
This prevents most memory growth in workloads that start many foreign threads. In the future, we could do even better by moving pages in the heap of an exited thread (and also maybe pooled stacks) elsewhere so they can be reused, and then also free the TLS object itself.
2 parents 936c8a6 + a125bc2 commit 9839aa3

File tree

4 files changed

+65
-0
lines changed

4 files changed

+65
-0
lines changed

src/gc-stacks.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ void sweep_stack_pools(void)
203203
assert(gc_n_threads);
204204
for (int i = 0; i < gc_n_threads; i++) {
205205
jl_ptls_t ptls2 = gc_all_tls_states[i];
206+
if (ptls2 == NULL)
207+
continue;
206208

207209
// free half of stacks that remain unused since last sweep
208210
for (int p = 0; p < JL_N_STACK_POOLS; p++) {
@@ -223,6 +225,12 @@ void sweep_stack_pools(void)
223225
void *stk = small_arraylist_pop(al);
224226
free_stack(stk, pool_sizes[p]);
225227
}
228+
if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) {
229+
small_arraylist_free(al);
230+
}
231+
}
232+
if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL) {
233+
small_arraylist_free(ptls2->heap.free_stacks);
226234
}
227235

228236
small_arraylist_t *live_tasks = &ptls2->heap.live_tasks;

src/gc.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,19 @@ void gc_free_pages(void)
17461746
}
17471747
}
17481748

1749+
void gc_move_to_global_page_pool(jl_gc_page_stack_t *pgstack)
1750+
{
1751+
while (1) {
1752+
jl_gc_pagemeta_t *pg = pop_lf_back(pgstack);
1753+
if (pg == NULL) {
1754+
break;
1755+
}
1756+
jl_gc_free_page(pg);
1757+
jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -GC_PAGE_SZ);
1758+
push_lf_back(&global_page_pool_freed, pg);
1759+
}
1760+
}
1761+
17491762
// setup the data-structures for a sweep over all memory pools
17501763
static void gc_sweep_pool(void)
17511764
{
@@ -3775,6 +3788,24 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection)
37753788
else {
37763789
ptls2->heap.remset->len = 0;
37773790
}
3791+
// free empty GC state for threads that have exited
3792+
if (jl_atomic_load_relaxed(&ptls2->current_task) == NULL &&
3793+
(ptls->tid < gc_first_tid || ptls2->tid >= gc_first_tid + jl_n_gcthreads)) {
3794+
jl_thread_heap_t *heap = &ptls2->heap;
3795+
if (heap->weak_refs.len == 0)
3796+
small_arraylist_free(&heap->weak_refs);
3797+
if (heap->live_tasks.len == 0)
3798+
small_arraylist_free(&heap->live_tasks);
3799+
if (heap->remset->len == 0)
3800+
arraylist_free(heap->remset);
3801+
if (heap->last_remset->len == 0)
3802+
arraylist_free(heap->last_remset);
3803+
if (ptls2->finalizers.len == 0)
3804+
arraylist_free(&ptls2->finalizers);
3805+
if (ptls2->sweep_objs.len == 0)
3806+
arraylist_free(&ptls2->sweep_objs);
3807+
gc_move_to_global_page_pool(&ptls2->page_metadata_buffered);
3808+
}
37783809
}
37793810

37803811
#ifdef __GLIBC__
@@ -3993,6 +4024,18 @@ void jl_init_thread_heap(jl_ptls_t ptls)
39934024
jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval);
39944025
}
39954026

4027+
void jl_free_thread_gc_state(jl_ptls_t ptls)
4028+
{
4029+
jl_gc_markqueue_t *mq = &ptls->mark_queue;
4030+
ws_queue_t *cq = &mq->chunk_queue;
4031+
free_ws_array(jl_atomic_load_relaxed(&cq->array));
4032+
jl_atomic_store_relaxed(&cq->array, NULL);
4033+
ws_queue_t *q = &mq->ptr_queue;
4034+
free_ws_array(jl_atomic_load_relaxed(&q->array));
4035+
jl_atomic_store_relaxed(&q->array, NULL);
4036+
arraylist_free(&mq->reclaim_set);
4037+
}
4038+
39964039
// System-wide initializations
39974040
void jl_gc_init(void)
39984041
{

src/threading.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ JL_DLLEXPORT jl_gcframe_t **jl_adopt_thread(void)
435435
void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT;
436436
void scheduler_delete_thread(jl_ptls_t ptls) JL_NOTSAFEPOINT;
437437

438+
void jl_free_thread_gc_state(jl_ptls_t ptls);
439+
438440
static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER
439441
{
440442
#ifndef _OS_WINDOWS_
@@ -508,6 +510,12 @@ static void jl_delete_thread(void *value) JL_NOTSAFEPOINT_ENTER
508510
#else
509511
pthread_mutex_unlock(&in_signal_lock);
510512
#endif
513+
free(ptls->bt_data);
514+
small_arraylist_free(&ptls->locks);
515+
ptls->previous_exception = NULL;
516+
// allow the page root_task is on to be freed
517+
ptls->root_task = NULL;
518+
jl_free_thread_gc_state(ptls);
511519
// then park in safe-region
512520
(void)jl_gc_safe_enter(ptls);
513521
}

src/work-stealing-queue.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ static inline ws_array_t *create_ws_array(size_t capacity, int32_t eltsz) JL_NOT
3434
return a;
3535
}
3636

37+
static inline void free_ws_array(ws_array_t *a)
38+
{
39+
free(a->buffer);
40+
free(a);
41+
}
42+
3743
typedef struct {
3844
_Atomic(int64_t) top;
3945
char _padding[JL_CACHE_BYTE_ALIGNMENT - sizeof(_Atomic(int64_t))];

0 commit comments

Comments
 (0)