Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →
class method
GC.verify_compaction_references
(?toward: :empty | untyped, ?double_heap: ::boolish, ?expand_heap: ::boolish) -> ::GC::compact_info
GC.verify_compaction_references(toward: nil, double_heap: false) → hash
gc/default/default.c
View on GitHub ↗
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);
}
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.
See also
.total_time
.verify_internal_consistency
.stress=
#garbage_collect
also in
3.3 · 3.2 · 3.1 · 3.0