module GC

The GC module provides an interface to Ruby’s mark-and-sweep garbage collection mechanism.

Some of the underlying methods are also available via the ObjectSpace module.

You may obtain information about the operation of the GC through GC::Profiler.

Constants

INTERNAL_CONSTANTS

Internal constants in the garbage collector.

OPTS

GC build options

Public Class Methods

Raises NoMemoryError when allocating an instance of the given classes.

static VALUE
rb_gcdebug_add_stress_to_class(int argc, VALUE *argv, VALUE self)
{
    rb_objspace_t *objspace = rb_gc_get_objspace();

    if (!stress_to_class) {
        set_stress_to_class(rb_ident_hash_new_capa(argc));
    }

    for (int i = 0; i < argc; i++) {
        VALUE klass = argv[i];
        rb_hash_aset(stress_to_class, klass, Qtrue);
    }

    return self;
}

Returns whether or not automatic compaction has been enabled.

static VALUE
gc_get_auto_compact(VALUE _)
{
    return ruby_enable_autocompact ? Qtrue : Qfalse;
}

Updates automatic compaction mode.

When enabled, the compactor will execute on every major collection.

Enabling compaction will degrade performance on major collections.

static VALUE
gc_set_auto_compact(VALUE _, VALUE v)
{
    GC_ASSERT(GC_COMPACTION_SUPPORTED);

    ruby_enable_autocompact = RTEST(v);

#if RGENGC_CHECK_MODE
    ruby_autocompact_compare_func = NULL;

    if (SYMBOL_P(v)) {
        ID id = RB_SYM2ID(v);
        if (id == rb_intern("empty")) {
            ruby_autocompact_compare_func = compare_free_slots;
        }
    }
#endif

    return v;
}

This function compacts objects together in Ruby’s heap. It eliminates unused space (or fragmentation) in the heap by moving objects in to that unused space. If there is more than 1 running Ractor, it runs a global GC compaction (all object spaces).

The returned hash contains statistics about the objects that were moved; see GC.latest_compact_info.

This method is only expected to work on CRuby.

To test whether GC compaction is supported, use the idiom:

GC.respond_to?(:compact)
static VALUE
gc_compact(VALUE self)
{
    rb_objspace_t *objspace = rb_gc_get_objspace();
    int full_marking_p = gc_config_full_mark_val;
    gc_config_full_mark_set(TRUE);

    /* Run GC with compaction enabled */
    rb_gc_impl_start(rb_gc_get_objspace(), true, true, true, true, true);
    gc_config_full_mark_set(full_marking_p);

    return gc_compact_stats(self);
}

This method is implementation-specific to CRuby.

Sets or gets information about the current GC configuration.

Configuration parameters are GC implementation-specific and may change without notice.

With no argument given, returns a hash containing the configuration:

GC.config
# => {rgengc_allow_full_mark: true, implementation: "default"}

With argument hash_to_merge given, merges that hash into the stored configuration hash; ignores unknown hash keys; returns the configuration hash:

GC.config(rgengc_allow_full_mark: false)
# => {rgengc_allow_full_mark: false, implementation: "default"}
GC.config(foo: 'bar')
# => {rgengc_allow_full_mark: false, implementation: "default"}

All-Implementations Configuration

The single read-only entry for all implementations is:

  • :implementation: the string name of the implementation; for the Ruby default implementation, 'default'.

Implementation-Specific Configuration

A GC implementation maintains its own implementation-specific configuration.

For Ruby’s default implementation the single entry is:

  • :rgengc_allow_full_mark: Controls whether the GC is allowed to run a full mark (young & old objects):

    • true (default): GC interleaves major and minor collections. A flag is set to notify GC that a full mark has been requested. This flag is accessible via GC.latest_gc_info(:need_major_by).

    • false: GC does not initiate a full marking cycle unless explicitly directed by user code; see GC.start. Setting this parameter to false disables young-to-old promotion. For performance reasons, we recommended warming up the application using Process.warmup before setting this parameter to false.

# File gc.rb, line 534
def self.config hash = nil
  if Primitive.cexpr!("RBOOL(RB_TYPE_P(hash, T_HASH))")
    if hash.include?(:implementation)
      raise ArgumentError, 'Attempting to set read-only key "Implementation"'
    end

    Primitive.gc_config_set hash
  elsif hash != nil
    raise ArgumentError
  end

  Primitive.gc_config_get
end

Returns the total number of times garbage collection has occurred:

With the default GC, counts the current Ractor’s collections.

GC.count # => 385
GC.start
GC.count # => 386
# File gc.rb, line 148
def self.count
  Primitive.gc_count
end

Disables garbage collection (but GC.start remains potent): returns whether garbage collection was already disabled.

GC.enable
GC.disable # => false
GC.disable # => true
# File gc.rb, line 97
def self.disable
  Primitive.gc_disable
end

Enables garbage collection; returns whether garbage collection was disabled:

GC.disable
GC.enable # => true
GC.enable # => false
# File gc.rb, line 83
def self.enable
  Primitive.gc_enable
end

Returns information about object moved in the most recent GC compaction.

The returned hash contains the following keys:

considered

Hash containing the type of the object as the key and the number of objects of that type that were considered for movement.

moved

Hash containing the type of the object as the key and the number of objects of that type that were actually moved.

moved_up

Hash containing the type of the object as the key and the number of objects of that type that were increased in size.

moved_down

Hash containing the type of the object as the key and the number of objects of that type that were decreased in size.

Some objects can’t be moved (due to pinning) so these numbers can be used to calculate compaction efficiency.

static VALUE
gc_compact_stats(VALUE self)
{
    rb_objspace_t *objspace = rb_gc_get_objspace();
    VALUE h = rb_hash_new();
    VALUE considered = rb_hash_new();
    VALUE moved = rb_hash_new();
    VALUE moved_up = rb_hash_new();
    VALUE moved_down = rb_hash_new();

    for (size_t i = 0; i < T_MASK; i++) {
        if (objspace->rcompactor.considered_count_table[i]) {
            rb_hash_aset(considered, type_sym(i), SIZET2NUM(objspace->rcompactor.considered_count_table[i]));
        }

        if (objspace->rcompactor.moved_count_table[i]) {
            rb_hash_aset(moved, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_count_table[i]));
        }

        if (objspace->rcompactor.moved_up_count_table[i]) {
            rb_hash_aset(moved_up, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_up_count_table[i]));
        }

        if (objspace->rcompactor.moved_down_count_table[i]) {
            rb_hash_aset(moved_down, type_sym(i), SIZET2NUM(objspace->rcompactor.moved_down_count_table[i]));
        }
    }

    rb_hash_aset(h, ID2SYM(rb_intern("considered")), considered);
    rb_hash_aset(h, ID2SYM(rb_intern("moved")), moved);
    rb_hash_aset(h, ID2SYM(rb_intern("moved_up")), moved_up);
    rb_hash_aset(h, ID2SYM(rb_intern("moved_down")), moved_down);

    return h;
}

With no argument given, returns information about the most recent garbage collection:

GC.latest_gc_info
# =>
{major_by: :force,
 need_major_by: nil,
 gc_by: :method,
 have_finalizer: false,
 immediate_sweep: true,
 state: :none,
 weak_references_count: 0,
 retained_weak_references_count: 0}

With symbol argument key given, returns the value for that key:

GC.latest_gc_info(:gc_by) # => :newobj

With hash argument hash given, returns that hash with GC information merged into its content; this form may be useful in minimizing probe effects:

h = {foo: 0, bar: 1}
GC.latest_gc_info(h)
# =>
{foo: 0,
 bar: 1,
 major_by: nil,
 need_major_by: nil,
 gc_by: :newobj,
 have_finalizer: false,
 immediate_sweep: false,
 state: :sweeping,
 weak_references_count: 0,
 retained_weak_references_count: 0}
# File gc.rb, line 590
def self.latest_gc_info hash_or_key = nil
  if hash_or_key == nil
    hash_or_key = {}
  elsif Primitive.cexpr!("RBOOL(!SYMBOL_P(hash_or_key) && !RB_TYPE_P(hash_or_key, T_HASH))")
    raise TypeError, "non-hash or symbol given"
  end

  Primitive.cstmt! %{
    return rb_gc_latest_gc_info(hash_or_key);
  }
end

Returns the size of memory allocated by malloc().

Only available if ruby was built with CALC_EXACT_MALLOC_SIZE.

static VALUE
gc_malloc_allocated_size(VALUE self)
{
    rb_objspace_t *objspace = (rb_objspace_t *)rb_gc_get_objspace();
    return ULL2NUM(objspace->malloc_params.allocated_size);
}

Returns the number of malloc() allocations.

Only available if ruby was built with CALC_EXACT_MALLOC_SIZE.

static VALUE
gc_malloc_allocations(VALUE self)
{
    rb_objspace_t *objspace = (rb_objspace_t *)rb_gc_get_objspace();
    return ULL2NUM(objspace->malloc_params.allocations);
}

Returns the setting for GC total time measurement in the current Ractor; the main Ractor’s initial setting is true and, with the default GC, other Ractors inherit their creator’s setting. See GC.total_time.

# File gc.rb, line 645
def self.measure_total_time
  Primitive.cexpr! %{
    RBOOL(rb_gc_impl_get_measure_total_time(rb_gc_get_objspace()))
  }
end

Enables or disables GC total time measurement; returns setting. See GC.total_time.

When argument object is nil or false, disables total time measurement; GC.measure_total_time then returns false:

GC.measure_total_time = nil   # => nil
GC.measure_total_time         # => false
GC.measure_total_time = false # => false
GC.measure_total_time         # => false

Otherwise, enables total time measurement; GC.measure_total_time then returns true:

GC.measure_total_time = true # => true
GC.measure_total_time        # => true
GC.measure_total_time = :foo # => :foo
GC.measure_total_time        # => true

Note that when enabled, total time measurement affects performance.

The setting is per-Ractor: it affects only the Ractor that sets it. With the default GC, a Ractor created by Ractor.new inherits the setting of the Ractor that creates it; the main Ractor starts with measurement enabled.

# File gc.rb, line 631
def self.measure_total_time=(flag)
  Primitive.cstmt! %{
    rb_gc_impl_set_measure_total_time(rb_gc_get_objspace(), flag);
    return flag;
  }
end

No longer raises NoMemoryError when allocating an instance of the given classes.

static VALUE
rb_gcdebug_remove_stress_to_class(int argc, VALUE *argv, VALUE self)
{
    rb_objspace_t *objspace = rb_gc_get_objspace();

    if (stress_to_class) {
        for (int i = 0; i < argc; ++i) {
            rb_hash_delete(stress_to_class, argv[i]);
        }

        if (rb_hash_size(stress_to_class) == 0) {
            stress_to_class = 0;
        }
    }

    return Qnil;
}

Initiates garbage collection, even if explicitly disabled by GC.disable.

Keyword arguments:

  • full_mark: a boolean value specifying whether to perform a major garbage collection cycle:

    • true: initiates a major garbage collection cycle, meaning all objects (old and new) are marked. When another Ractor’s object space exists (a live Ractor, or a terminated one not yet absorbed), or one was absorbed since the last global collection, and global is true (the default), the cycle is a global collection instead; see global.

    • false: initiates a minor garbage collection cycle, meaning only young objects are marked.

  • immediate_mark: a boolean value specifying whether marking is performed incrementally:

    • true: marking is completed before the method returns.

    • false: marking is performed in parts, interleaved with program execution both before the method returns and afterward; therefore marking may not be completed before the return. Note that if full_mark is false, marking is always immediate, regardless of the value of immediate_mark.

  • immediate_sweep: a boolean value specifying whether sweeping is deferred (lazy sweep):

    • true: sweeping is completed before the method returns.

    • false: sweeping is performed in parts, interleaved with program execution both before the method returns and afterward; therefore sweeping may not be completed before the return.

  • global: a boolean value specifying, when another Ractor’s object space exists, whether to collect every Ractor’s heap together in a single stop-the-world mark/sweep cycle:

    • true: with another Ractor’s object space present (see full_mark) it runs a stop-the-world cycle and reclaims shareable and cross-Ractor garbage. This option, although it defaults to true, is ignored unless full_mark, immediate_mark and immediate_sweep are all true. With a single object space it has no effect and the other options apply as usual. This option is also ignored unless you’re using the default GC.

    • false: collect only the calling Ractor’s own objects. Shareable objects and cross-ractor garbage are not swept.

    GC.stat counts a local major collection under :major_gc_count, a local minor collection under :minor_gc_count, and a global collection under :global_gc_count for the calling Ractor.

Note that these keyword arguments are implementation- and version-dependent, are not guaranteed to be future-compatible, and may be ignored in some implementations.

# File gc.rb, line 64
def self.start full_mark: true, immediate_mark: true, immediate_sweep: true, global: true
  Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false, global
end

This method is implementation-specific to CRuby.

Returns GC statistics. The particular statistics are implementation-specific and may change in the future without notice.

With scope: :ractor (the default) or scope: :local, returns statistics for the current Ractor’s object space. With scope: :global, returns cumulative collection counts and measured GC time across all Ractors.

With no positional argument or nil given, returns a hash containing the GC statistics:

GC.stat
# =>
{count: 1,
 time: 0,
 marking_time: 0,
 sweeping_time: 0,
 total_malloc_bytes: 10432791,
 total_free_bytes: 6989873,
 heap_allocated_pages: 41,
 heap_empty_pages: 0,
 heap_allocatable_bytes: 228656,
 heap_eden_pages: 41,
 total_allocated_pages: 41,
 total_freed_pages: 0,
 malloc_increase_bytes: 0,
 malloc_increase_bytes_limit: 16777216,
 minor_gc_count: 0,
 major_gc_count: 1,
 global_gc_count: 0,
 compact_count: 0,
 read_barrier_faults: 0,
 total_moved_objects: 0,
 remembered_wb_unprotected_objects: 0,
 remembered_wb_unprotected_objects_limit: 7,
 old_objects: 721,
 old_objects_limit: 1442,
 oldmalloc_increase_bytes: 0,
 oldmalloc_increase_bytes_limit: 16777216,
 total_allocated_objects: 36735,
 total_freed_objects: 19527,
 heap_available_slots: 40591,
 heap_live_slots: 17208,
 heap_free_slots: 23383,
 heap_final_slots: 0,
 heap_marked_slots: 17175}

With symbol argument key given, returns the value for that key:

GC.stat(:count) # => 30

With hash argument hash given, returns that hash with GC statistics merged into its content; this form may be useful in minimizing probe effects:

h = {foo: 0, bar: 1}
GC.stat(h)
h.keys.take(5) # => [:foo, :bar, :count, :time, :marking_time]

The hash includes entries such as:

  • :count: The total number of garbage collections run since application start (the count includes minor, major, and global garbage collections).

  • :time: The total time spent in garbage collections (in milliseconds).

  • :marking_time: The total time spent marking objects (in milliseconds).

  • :sweeping_time: The total time spent sweeping objects (in milliseconds).

  • :total_malloc_bytes: The total number of bytes allocated by malloc since application start.

  • :total_free_bytes: The total number of bytes freed since application start.

  • :heap_allocated_pages: The total number of allocated pages.

  • :heap_empty_pages: The number of pages with no live objects, and that could be released to the system.

  • :heap_allocatable_bytes: The number of bytes that can be allocated without triggering a new garbage collection cycle.

  • :heap_eden_pages: The total number of pages which contain at least one live slot.

  • :total_allocated_pages: The cumulative number of pages allocated since application start.

  • :total_freed_pages: The cumulative number of pages freed since application start.

  • :malloc_increase_bytes: Amount of memory allocated on the heap for objects. Decreased by any GC.

  • :malloc_increase_bytes_limit: When :malloc_increase_bytes crosses this limit, GC is triggered.

  • :minor_gc_count: The total number of minor garbage collections run since process start.

  • :major_gc_count: The total number of major garbage collections run since process start. Global garbage collections are counted separately.

  • :global_gc_count: The number of global (stop-the-world) garbage collections this Ractor initiated. Each global garbage collection is attributed to the Ractor that initiated it, so the values sum across Ractors. With scope: :global, the process total, including terminated Ractors.

  • :compact_count: The total number of compactions run since process start.

  • :read_barrier_faults: The total number of times the read barrier was triggered during compaction.

  • :total_moved_objects: The total number of objects compaction has moved.

  • :remembered_wb_unprotected_objects: The total number of objects without write barriers.

  • :remembered_wb_unprotected_objects_limit: When :remembered_wb_unprotected_objects crosses this limit, major GC is triggered.

  • :old_objects: Number of live, old objects which have survived at least 3 garbage collections.

  • :old_objects_limit: When :old_objects crosses this limit, major GC is triggered.

  • :oldmalloc_increase_bytes: Amount of memory allocated on the heap for objects. Decreased by major GC.

  • :oldmalloc_increase_bytes_limit: When :oldmalloc_increase_bytes crosses this limit, major GC is triggered.

  • :total_allocated_objects: The cumulative number of objects allocated since application start.

  • :total_freed_objects: The cumulative number of objects freed since application start.

  • :heap_available_slots: The total number of slots in all :heap_allocated_pages.

  • :heap_live_slots: The total number of slots which contain live objects.

  • :heap_free_slots: The total number of slots which do not contain live objects.

  • :heap_final_slots: The total number of slots with pending finalizers to be run.

  • :heap_marked_slots: The total number of objects marked in the last GC.

Global scope ↑

With scope: :global, the default collector reports cumulative collection counts and measured CPU time across all Ractors, retaining history from destroyed object spaces:

GC.stat(scope: :global)
# => {count: 42, minor_gc_count: 32, major_gc_count: 8,
#     global_gc_count: 2,
#     time: 3, marking_time: 2, sweeping_time: 1}

These totals include both local and global collections, and :count equals :minor_gc_count plus :major_gc_count plus :global_gc_count in both scopes.

:global_gc_count attributes each global collection to the Ractor that initiated it, and the total retains the counts of terminated Ractors.

:time may exceed :marking_time plus :sweeping_time by one millisecond because raw nanoseconds are summed before conversion. Times measure collector CPU work, not wall-clock pauses.

Reads combine object-space publications without requesting a stop-the-world barrier. GC.measure_total_time controls future timing accumulation. Profiler controls do not affect these totals. A forked child inherits its parent’s totals. MMTk and wbcheck raise NotImplementedError for this scope.

Unknown scopes and unavailable keys raise ArgumentError.

# File gc.rb, line 324
def self.stat hash_or_key = nil, scope: :ractor
  case scope
  when :ractor, :local
    Primitive.gc_stat hash_or_key, false
  when :global
    Primitive.gc_stat hash_or_key, true
  else
    raise ArgumentError, "unknown scope: #{scope.inspect}"
  end
end

This method is implementation-specific to CRuby.

Returns statistics for GC heaps. The particular statistics are implementation-specific and may change in the future without notice.

With no argument given, returns statistics for all heaps:

GC.stat_heap
# =>
{0 =>
  {slot_size: 32,
   heap_eden_pages: 24,
   heap_eden_slots: 12288,
   total_allocated_pages: 24,
   force_major_gc_count: 0,
   force_incremental_marking_finish_count: 0,
   total_allocated_objects: 8450,
   total_freed_objects: 3120},
 1 =>
  {slot_size: 64,
   heap_eden_pages: 246,
   heap_eden_slots: 402802,
   total_allocated_pages: 246,
   force_major_gc_count: 2,
   force_incremental_marking_finish_count: 1,
   total_allocated_objects: 33867152,
   total_freed_objects: 33520523},
 2 =>
  {slot_size: 128,
   heap_eden_pages: 84,
   heap_eden_slots: 68746,
   total_allocated_pages: 84,
   force_major_gc_count: 1,
   force_incremental_marking_finish_count: 4,
   total_allocated_objects: 147491,
   total_freed_objects: 90699},
 3 =>
  {slot_size: 256,
   heap_eden_pages: 157,
   heap_eden_slots: 64182,
   total_allocated_pages: 157,
   force_major_gc_count: 0,
   force_incremental_marking_finish_count: 0,
   total_allocated_objects: 211460,
   total_freed_objects: 190075},
 4 =>
  {slot_size: 512,
   heap_eden_pages: 8,
   heap_eden_slots: 1631,
   total_allocated_pages: 8,
   force_major_gc_count: 0,
   force_incremental_marking_finish_count: 0,
   total_allocated_objects: 1422,
   total_freed_objects: 700},
 5 =>
  {slot_size: 1024,
   heap_eden_pages: 16,
   heap_eden_slots: 1628,
   total_allocated_pages: 16,
   force_major_gc_count: 0,
   force_incremental_marking_finish_count: 0,
   total_allocated_objects: 1230,
   total_freed_objects: 309}}

In the example above, the keys in the outer hash are the heap identifiers:

GC.stat_heap.keys # => [0, 1, 2, 3, 4, 5]

On CRuby, each heap identifier is an integer; on other implementations, a heap identifier may be a string.

With only argument heap_id given, returns statistics for the given heap identifier:

GC.stat_heap(3)
# =>
{slot_size: 256,
 heap_eden_pages: 157,
 heap_eden_slots: 64182,
 total_allocated_pages: 157,
 force_major_gc_count: 0,
 force_incremental_marking_finish_count: 0,
 total_allocated_objects: 225018,
 total_freed_objects: 206647}

With arguments heap_id and key given, returns the value for the given key in the given heap:

GC.stat_heap(3, :slot_size) # => 256

With arguments nil and hash given, merges the statistics for all heaps into the given hash:

h = {foo: 0, bar: 1}
GC.stat_heap(nil, h).keys # => [:foo, :bar, 0, 1, 2, 3, 4]

With arguments heap_id and hash given, merges the statistics for the given heap into the given hash:

h = {foo: 0, bar: 1}
GC.stat_heap(2, h).keys
# =>
[:foo,
 :bar,
 :slot_size,
 :heap_eden_pages,
 :heap_eden_slots,
 :total_allocated_pages,
 :force_major_gc_count,
 :force_incremental_marking_finish_count,
 :total_allocated_objects,
 :total_freed_objects]

The statistics for a heap may include:

  • :slot_size: The slot size of the heap in bytes.

  • :heap_allocatable_pages: The number of pages that can be allocated without triggering a new garbage collection cycle.

  • :heap_eden_pages: The number of pages in the eden heap.

  • :heap_eden_slots: The total number of slots in all of the pages in the eden heap.

  • :total_allocated_pages: The total number of pages that have been allocated in the heap.

  • :total_freed_pages: The total number of pages that have been freed and released back to the system in the heap.

  • :force_major_gc_count: The number of times this heap has forced major garbage collection cycles to start due to running out of free slots.

  • :force_incremental_marking_finish_count: The number of times this heap has forced incremental marking to complete due to running out of pooled slots.

# File gc.rb, line 479
def self.stat_heap heap_name = nil, hash_or_key = nil
  Primitive.gc_stat_heap heap_name, hash_or_key
end

Returns the current GC stress-mode setting, which initially is false.

The stress mode may be set by method GC.stress=.

# File gc.rb, line 108
def self.stress
  Primitive.gc_stress_get
end

Enables or disables stress mode. Stress mode causes GC to be invoked at every GC opportunity (all memory and object allocations); it is intended only for debugging and severely degrades performance.

Sets the current GC stress mode to the given value:

  • If the value is nil or false, disables stress mode.

  • If the value is an integer, enables stress mode with certain flags; see below.

  • Otherwise, enables stress mode without any flags.

The flags are bits in the given integer:

  • 0x01: no major GC.

  • 0x02: no immediate sweep.

  • 0x04: full mark after malloc/calloc/realloc.

# File gc.rb, line 133
def self.stress=(flag)
  Primitive.gc_stress_set_m flag
end

Returns the GC total time in nanoseconds:

With the default collector, this is the current Ractor’s measured GC CPU work, not wall-clock pause time.

GC.total_time # => 156250

Note that total time accumulates only when total time measurement is enabled (that is, when GC.measure_total_time is true):

GC.measure_total_time # => true
GC.total_time # => 625000
GC.start
GC.total_time # => 937500
GC.start
GC.total_time # => 1093750

GC.measure_total_time = false
GC.total_time # => 1250000
GC.start
GC.total_time # => 1250000
GC.start
GC.total_time # => 1250000

GC.measure_total_time = true
GC.total_time # => 1250000
GC.start
GC.total_time # => 1406250
# File gc.rb, line 684
def self.total_time
  Primitive.cexpr! %{
    ULL2NUM(rb_gc_impl_get_total_time(rb_gc_get_objspace()))
  }
end

Verify compaction reference consistency.

This method is implementation specific. During compaction, objects that were moved are replaced with T_MOVED objects. No object should have a reference to a T_MOVED object after compaction.

This function expands the heap to ensure room to move all objects, compacts the heap to make sure everything moves, updates all references, then performs a full GC. If any object contains a reference to a T_MOVED object, that object should be pushed on the mark stack, and will make a SEGV.

static VALUE
gc_verify_compaction_references(int argc, VALUE* argv, VALUE self)
{
    static ID keywords[3] = {0};
    if (!keywords[0]) {
        keywords[0] = rb_intern("toward");
        keywords[1] = rb_intern("double_heap");
        keywords[2] = rb_intern("expand_heap");
    }

    VALUE options;
    rb_scan_args_kw(rb_keyword_given_p(), argc, argv, ":", &options);

    VALUE arguments[3] = { Qnil, Qfalse, Qfalse };
    int kwarg_count = rb_get_kwargs(options, keywords, 0, 3, arguments);
    bool toward_empty = kwarg_count > 0 && SYMBOL_P(arguments[0]) && SYM2ID(arguments[0]) == rb_intern("empty");
    bool expand_heap = (kwarg_count > 1 && RTEST(arguments[1])) || (kwarg_count > 2 && RTEST(arguments[2]));

    rb_objspace_t *objspace = rb_gc_get_objspace();

    /* This verification machinery (heap expansion, toward_empty page ordering, the
     * moved-reference walk) is built for a single objspace, so with several demote it
     * to a plain full GC.  Plain GC.compact does compact them via the global GC. */
    if (!rb_gc_single_objspace_p()) {
        rb_gc_impl_start(objspace, true, true, true, false, false);
        return gc_compact_stats(self);
    }

    /* Clear the heap. */
    rb_gc_impl_start(objspace, true, true, true, false, false);

    unsigned int lev = RB_GC_VM_LOCK();
    {
        gc_rest(objspace);

        /* if both double_heap and expand_heap are set, expand_heap takes precedence */
        if (expand_heap) {
            struct desired_compaction_pages_i_data desired_compaction = {
                .objspace = objspace,
                .required_slots = {0},
            };
            /* Work out how many objects want to be in each size pool, taking account of moves */
            objspace_each_pages(objspace, desired_compaction_pages_i, &desired_compaction, TRUE);

            /* Find out which pool has the most pages */
            size_t max_existing_pages = 0;
            for (int i = 0; i < HEAP_COUNT; i++) {
                rb_heap_t *heap = &heaps[i];
                max_existing_pages = MAX(max_existing_pages, heap->total_pages);
            }

            /* Add pages to each size pool so that compaction is guaranteed to move every object */
            for (int i = 0; i < HEAP_COUNT; i++) {
                rb_heap_t *heap = &heaps[i];

                size_t pages_to_add = 0;
                /*
                 * Step 1: Make sure every pool has the same number of pages, by adding empty pages
                 * to smaller pools. This is required to make sure the compact cursor can advance
                 * through all of the pools in `gc_sweep_compact` without hitting the "sweep &
                 * compact cursors met" condition on some pools before fully compacting others
                 */
                pages_to_add += max_existing_pages - heap->total_pages;
                /*
                 * Step 2: Now add additional free pages to each size pool sufficient to hold all objects
                 * that want to be in that size pool, whether moved into it or moved within it
                 */
                objspace->heap_pages.allocatable_bytes = desired_compaction.required_slots[i] * heap->slot_size;
                while (objspace->heap_pages.allocatable_bytes > 0) {
                    heap_page_allocate_and_initialize(objspace, heap);
                }
                /*
                 * Step 3: Add two more pages so that the compact & sweep cursors will meet _after_ all objects
                 * have been moved, and not on the last iteration of the `gc_sweep_compact` loop
                 */
                pages_to_add += 2;

                for (; pages_to_add > 0; pages_to_add--) {
                    heap_page_allocate_and_initialize_force(objspace, heap);
                }
            }
        }

        if (toward_empty) {
            objspace->rcompactor.compare_func = compare_free_slots;
        }
    }
    RB_GC_VM_UNLOCK(lev);

    rb_gc_impl_start(rb_gc_get_objspace(), true, true, true, true, false);

    rb_objspace_reachable_objects_from_root(root_obj_check_moved_i, objspace);
    objspace_each_objects(objspace, heap_check_moved_i, objspace, TRUE);

    objspace->rcompactor.compare_func = NULL;

    return gc_compact_stats(self);
}

Verifies internal consistency of the GC. This method should only be used for debugging.

This method is only expected to work on CRuby.

static VALUE
gc_verify_internal_consistency_m(VALUE dummy)
{
    rb_gc_verify_internal_consistency();
    return Qnil;
}

Public Instance Methods

Alias of GC.start

# File gc.rb, line 69
def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true, global: true
  Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false, global
end