Does RUBY_GC_MALLOC_LIMIT affect the frequency of Major GC cycles?
0 reputation · 27 Apr 2021, 01:51 UTC
0 reputation · 27 Apr 2021, 01:51 UTC
Ruby's generational garbage collector uses specific thresholds to determine when to trigger a Minor GC versus a full Major GC. While RUBY_GC_MALLOC_LIMIT is used to tune the threshold for memory allocations that trigger a GC run, the relationship between this limit and the promotion of objects to the old generation is not immediately clear.
In a high-allocation environment using MRI Ruby, there is a need to balance memory throughput with the stop-the-world pauses associated with Major GC. If the malloc limit is increased to reduce the frequency of GC cycles, it is uncertain whether this also delays the triggering of a Major GC or simply increases the volume of objects processed during each cycle.
RUBY_GC_MALLOC_LIMIT directly reduce the frequency of Major GC events?No — not directly. In MRI/CRuby's generational collector, RUBY_GC_MALLOC_LIMIT controls the malloc-allocation threshold that triggers a minor (young-generation) GC. Major GC frequency is governed primarily by RUBY_GC_OLDMALLOC_LIMIT, plus old-generation heap growth and the promotion of surviving objects. Raising RUBY_GC_MALLOC_LIMIT makes minor GCs less frequent; it does not, by itself, change the threshold at which a Major GC fires.
There is a real but indirect relationship, and it is workload-dependent:
So increasing RUBY_GC_MALLOC_LIMIT changes the volume per cycle and the minor-cycle cadence, but any change in Major GC frequency is a side effect of altered promotion behavior, not a direct control. You should not rely on it for that purpose.
If the goal is fewer stop-the-world Major GC pauses, the relevant knobs are:
RUBY_GC_OLDMALLOC_LIMIT — the old-generation malloc threshold that directly gates Major GC.RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR on versions that expose it).All of these variables are read once at process start, are process-wide, and apply only to MRI/CRuby — JRuby and TruffleRuby use entirely different GC tuning mechanisms. Exact defaults and even which knobs exist vary across Ruby 2.1 through 3.x, so check the documented defaults for your specific version before tuning.
GC.stat exposes the counters you need to separate minor from major cycles:
GC.stat.values_at(:count, :minor_gc_count, :major_gc_count)
Sample these before and after a representative workload run:
:major_gc_count and :minor_gc_count with default settings.RUBY_GC_MALLOC_LIMIT and re-measure. Expect :minor_gc_count to move; observe whether :major_gc_count shifts at all.RUBY_GC_OLDMALLOC_LIMIT and re-measure to isolate which knob actually moves Major GC frequency.One caution: lowering RUBY_GC_MALLOC_LIMIT aggressively to suppress promotion increases total GC time and can hurt throughput even if Major GCs become rarer. Measure wall-clock time and pause distribution, not just cycle counts.
The promotion behavior described above is well-established generational GC design in CRuby, but the magnitude of the indirect effect depends on your allocation profile. The verification steps above are the reliable way to confirm it for your application rather than assuming a directional effect.
Use comments to ask for clarification. Post a solution as an answer.
29,775 reputation · 27 Apr 2021, 07:59 UTC
Increasing RUBY_GC_MALLOC_LIMIT raises the allocation counter that must be exceeded before the young‑generation (minor) GC runs. Because the counter is reset after each minor collection, a higher limit means fewer minor GCs per unit time, but each minor GC has to scan a larger young‑generation heap. More objects survive each scan, so the promotion rate to the old generation can rise, which in turn feeds the old‑generation occupancy counters that trigger a Major GC. Thus the effect on Major GC frequency is indirect and workload‑dependent: if most allocations are short‑lived, raising the limit may barely change promotion; if many objects have medium lifetimes, the higher limit can increase Major GC frequency.