Skip to content

Commit

Permalink
expose metric to report reasons why full GCs were triggered
Browse files Browse the repository at this point in the history
  • Loading branch information
d-netto committed Sep 20, 2024
1 parent 44bef0d commit e2a67b1
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 2 deletions.
13 changes: 13 additions & 0 deletions base/timing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ function gc_page_utilization_data()
return Base.unsafe_wrap(Array, page_utilization_raw, JL_GC_N_MAX_POOLS, own=false)
end

# must be kept in sync with `src/gc-stock.h``
const FULL_GC_REASONS = [:FULL_GC_REASON_SWEEP_ALWAYS_FULL, :FULL_GC_REASON_FORCED_FULL_SWEEP,
:FULL_GC_REASON_USER_MAX_EXCEEDED, :FULL_GC_REASON_LARGE_PROMOTION_RATE]
function full_gc_reasons()
reason = cglobal(:full_sweep_reasons, UInt64)
reasons_as_array = Base.unsafe_wrap(Vector{UInt64}, reason, length(FULL_GC_REASONS), own=false)
d = Dict{Symbol, UInt64}()
for (i, r) in enumerate(FULL_GC_REASONS)
d[r] = reasons_as_array[i]
end
return d
end

"""
Base.jit_total_bytes()
Expand Down
15 changes: 13 additions & 2 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ uv_sem_t gc_sweep_assists_needed;
uv_mutex_t gc_queue_observer_lock;
// Tag for sentinel nodes in bigval list
uintptr_t gc_bigval_sentinel_tag;
// Table recording number of full GCs due to each reason
JL_DLLEXPORT uint64_t full_sweep_reasons[FULL_GC_NUM_REASONS];

// Flag that tells us whether we need to support conservative marking
// of objects.
Expand Down Expand Up @@ -3043,10 +3045,12 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection)
// we either free some space or get an OOM error.
if (gc_sweep_always_full) {
sweep_full = 1;
gc_count_full_sweep_reason(FULL_GC_REASON_SWEEP_ALWAYS_FULL);
}
if (collection == JL_GC_FULL && !prev_sweep_full) {
sweep_full = 1;
recollect = 1;
gc_count_full_sweep_reason(FULL_GC_REASON_FORCED_FULL_SWEEP);
}
if (sweep_full) {
// these are the difference between the number of gc-perm bytes scanned
Expand Down Expand Up @@ -3182,10 +3186,17 @@ static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection)
}

double old_ratio = (double)promoted_bytes/(double)heap_size;
if (heap_size > user_max || old_ratio > 0.15)
if (heap_size > user_max) {
next_sweep_full = 1;
else
gc_count_full_sweep_reason(FULL_GC_REASON_USER_MAX_EXCEEDED);
}
else if (old_ratio > 0.15) {
next_sweep_full = 1;
gc_count_full_sweep_reason(FULL_GC_REASON_LARGE_PROMOTION_RATE);
}
else {
next_sweep_full = 0;
}
if (heap_size > user_max || thrashing)
under_pressure = 1;
// sweeping is over
Expand Down
14 changes: 14 additions & 0 deletions src/gc-stock.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,20 @@ FORCE_INLINE void gc_big_object_link(bigval_t *sentinel_node, bigval_t *node) JL
sentinel_node->next = node;
}

// Must be kept in sync with `base/timing.jl`
#define FULL_GC_REASON_SWEEP_ALWAYS_FULL (0)
#define FULL_GC_REASON_FORCED_FULL_SWEEP (1)
#define FULL_GC_REASON_USER_MAX_EXCEEDED (2)
#define FULL_GC_REASON_LARGE_PROMOTION_RATE (3)
#define FULL_GC_NUM_REASONS (4)

extern JL_DLLEXPORT uint64_t full_sweep_reasons[FULL_GC_NUM_REASONS];
STATIC_INLINE void gc_count_full_sweep_reason(int reason) JL_NOTSAFEPOINT
{
assert(reason >= 0 && reason < FULL_GC_NUM_REASONS);
full_sweep_reasons[reason]++;
}

extern uv_mutex_t gc_perm_lock;
extern uv_mutex_t gc_threads_lock;
extern uv_cond_t gc_threads_cond;
Expand Down
13 changes: 13 additions & 0 deletions test/gc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ function issue_54275_test()
@test !live_bytes_has_grown_too_much
end

function full_gc_reasons_test()
n_full_gcs = 8
for _ in 1:n_full_gcs
GC.gc(true)
end
reasons = Base.full_gc_reasons()
@test reasons[:FULL_GC_REASON_FORCED_FULL_SWEEP] >= (n_full_gcs - 1)
end

# !!! note:
# Since we run our tests on 32bit OS as well we confine ourselves
# to parameters that allocate about 512MB of objects. Max RSS is lower
Expand All @@ -72,3 +81,7 @@ end
@testset "Base.GC docstrings" begin
@test isempty(Docs.undocumented_names(GC))
end

@testset "Full GC reasons" begin
@test full_gc_reasons_test()
end

0 comments on commit e2a67b1

Please sign in to comment.