diff --git a/base/gcutils.jl b/base/gcutils.jl index 8162f9e9ddf1f..113ba91436f80 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -49,15 +49,29 @@ Module with garbage collection utilities. """ module GC +# @enum-like structure +struct CollectionType + x::Int +end +Base.cconvert(::Type{Cint}, collection::CollectionType) = Cint(collection.x) + +const Auto = CollectionType(0) +const Full = CollectionType(1) +const Incremental = CollectionType(2) + """ - GC.gc() + GC.gc(full::Bool=true) + GC.gc(collection::CollectionType) -Perform garbage collection. +Perform garbage collection. The argument `full` determines whether a full, but more costly +collection is performed. Otherwise, heuristics are used to determine which type of +collection is needed. For exact control, pass an argument of type `CollectionType`. !!! warning Excessive use will likely lead to poor performance. """ -gc(full::Bool=true) = ccall(:jl_gc_collect, Cvoid, (Int32,), full) +gc(full::Bool=true) = ccall(:jl_gc_collect, Cvoid, (Cint,), full) +gc(collection::CollectionType) = ccall(:jl_gc_collect, Cvoid, (Cint,), collection) """ GC.enable(on::Bool) diff --git a/src/gc.c b/src/gc.c index 35b50b25f6e8c..831dc14545348 100644 --- a/src/gc.c +++ b/src/gc.c @@ -778,7 +778,7 @@ void jl_gc_force_mark_old(jl_ptls_t ptls, jl_value_t *v) JL_NOTSAFEPOINT static inline void maybe_collect(jl_ptls_t ptls) { if (ptls->gc_num.allocd >= 0 || gc_debug_check_other()) { - jl_gc_collect(0); + jl_gc_collect(JL_GC_AUTO); } else { jl_gc_safepoint_(ptls); @@ -2667,7 +2667,7 @@ static void jl_gc_queue_bt_buf(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp size_t jl_maxrss(void); // Only one thread should be running in this function -static int _jl_gc_collect(jl_ptls_t ptls, int full) +static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection) { combine_thread_gc_counts(&gc_num); @@ -2697,7 +2697,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, int full) if (gc_cblist_root_scanner) { export_gc_state(ptls, &sp); gc_invoke_callbacks(jl_gc_cb_root_scanner_t, - gc_cblist_root_scanner, (full)); + gc_cblist_root_scanner, (collection)); import_gc_state(ptls, &sp); } gc_mark_loop(ptls, sp); @@ -2775,12 +2775,14 @@ static int _jl_gc_collect(jl_ptls_t ptls, int full) else if (live_bytes >= last_live_bytes) { grown_heap_age++; } - if ((full || large_frontier || + if (collection == JL_GC_INCREMENTAL) { + sweep_full = 0; + } else if ((collection == JL_GC_FULL || large_frontier || ((not_freed_enough || promoted_bytes >= gc_num.interval) && (promoted_bytes >= default_collect_interval || prev_sweep_full)) || grown_heap_age > 1) && gc_num.pause > 1) { - recollect = full; + recollect = (collection == JL_GC_FULL); if (large_frontier) gc_num.interval = last_long_collect_interval; if (not_freed_enough || large_frontier) { @@ -2869,7 +2871,7 @@ static int _jl_gc_collect(jl_ptls_t ptls, int full) return recollect; } -JL_DLLEXPORT void jl_gc_collect(int full) +JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) { jl_ptls_t ptls = jl_get_ptls_states(); if (jl_gc_disable_counter) { @@ -2896,12 +2898,13 @@ JL_DLLEXPORT void jl_gc_collect(int full) // no-op for non-threading jl_gc_wait_for_the_world(); gc_invoke_callbacks(jl_gc_cb_pre_gc_t, - gc_cblist_pre_gc, (full)); + gc_cblist_pre_gc, (collection)); if (!jl_gc_disable_counter) { JL_LOCK_NOGC(&finalizers_lock); - if (_jl_gc_collect(ptls, full)) { - int ret = _jl_gc_collect(ptls, 0); + if (_jl_gc_collect(ptls, collection)) { + // recollect + int ret = _jl_gc_collect(ptls, JL_GC_AUTO); (void)ret; assert(!ret); } @@ -2922,7 +2925,7 @@ JL_DLLEXPORT void jl_gc_collect(int full) ptls->in_finalizer = was_in_finalizer; } gc_invoke_callbacks(jl_gc_cb_post_gc_t, - gc_cblist_post_gc, (full)); + gc_cblist_post_gc, (collection)); } void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_mark_sp_t *sp) @@ -3332,7 +3335,7 @@ JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void) // properly. We don't have to worry about race conditions // for this part, as allocation itself is unproblematic and // a collection will wait for safepoints. - jl_gc_collect(1); + jl_gc_collect(JL_GC_FULL); } return result; } else { diff --git a/src/julia.h b/src/julia.h index 090984637fddb..c0192e4ee2d6d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -746,7 +746,13 @@ JL_DLLEXPORT int64_t jl_gc_total_bytes(void); JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void); JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void); -JL_DLLEXPORT void jl_gc_collect(int); +typedef enum { + JL_GC_AUTO = 0, // use heuristics to determine the collection type + JL_GC_FULL = 1, // force a full collection + JL_GC_INCREMENTAL = 2, // force an incremental collection +} jl_gc_collection_t; + +JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t); JL_DLLEXPORT void jl_gc_add_finalizer(jl_value_t *v, jl_function_t *f); JL_DLLEXPORT void jl_finalize(jl_value_t *o); diff --git a/src/staticdata.c b/src/staticdata.c index 60fbe7608357a..b500752ac2f77 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1269,8 +1269,8 @@ static void jl_cleanup_serializer2(void); static void jl_save_system_image_to_stream(ios_t *f) { - jl_gc_collect(1); // full - jl_gc_collect(0); // incremental (sweep finalizers) + jl_gc_collect(JL_GC_FULL); + jl_gc_collect(JL_GC_INCREMENTAL); // sweep finalizers JL_TIMING(SYSIMG_DUMP); int en = jl_gc_enable(0); jl_init_serializer2(1); diff --git a/test/misc.jl b/test/misc.jl index fc1cc309adc0f..8b5afee2719af 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -758,3 +758,12 @@ end # Finalizer with immutable should throw @test_throws ErrorException finalizer(x->nothing, 1) @test_throws ErrorException finalizer(C_NULL, 1) + + +@testset "GC utilities" begin + GC.gc() + GC.gc(true); GC.gc(false) + GC.gc(GC.Auto); GC.gc(GC.Full); GC.gc(GC.Incremental) + + GC.safepoint() +end