summaryrefslogtreecommitdiff
path: root/gc.c
AgeCommit message (Collapse)Author
2025-05-09Deprecate `ObjectSpace._id2ref`Jean Boussier
[Feature #15408] Matz decided to deprecate it for Ruby 2.6 or 2.7 but that never actually happened. Given the object_id table is a contention point for Ractors it seems sensible to finally deprecate this API so we can generate and store object ids more efficiently in the future. Notes: Merged: https://github.com/ruby/ruby/pull/13157
2025-05-08Refactor `id_to_obj_tbl` compactionJean Boussier
Use `st_foreach_with_replace` rather than to call `st_insert` from inside `st_foreach`, this saves from having to disable GC. Co-Authored-By: Peter Zhu <[email protected]> Notes: Merged: https://github.com/ruby/ruby/pull/13159
2025-05-08Get rid of RB_GC_VM_ID_TO_OBJ_TABLE_KEYSJean Boussier
Notes: Merged: https://github.com/ruby/ruby/pull/13159
2025-05-08Move `object_id` in object fields.Jean Boussier
And get rid of the `obj_to_id_tbl` It's no longer needed, the `object_id` is now stored inline in the object alongside instance variables. We still need the inverse table in case `_id2ref` is invoked, but we lazily build it by walking the heap if that happens. The `object_id` concern is also no longer a GC implementation concern, but a generic implementation. Co-Authored-By: Matt Valentine-House <[email protected]> Notes: Merged: https://github.com/ruby/ruby/pull/13159
2025-05-08Refactor OBJ_TOO_COMPLEX_SHAPE_ID to not be referenced outside shape.hJean Boussier
Also refactor checks for `->type == SHAPE_OBJ_TOO_COMPLEX`. Notes: Merged: https://github.com/ruby/ruby/pull/13159
2025-05-08Rename `ivptr` -> `fields`, `next_iv_index` -> `next_field_index`Jean Boussier
Ivars will longer be the only thing stored inline via shapes, so keeping the `iv_index` and `ivptr` names would be confusing. Instance variables won't be the only thing stored inline via shapes, so keeping the `ivptr` name would be confusing. `field` encompass anything that can be stored in a VALUE array. Similarly, `gen_ivtbl` becomes `gen_fields_tbl`. Notes: Merged: https://github.com/ruby/ruby/pull/13159
2025-05-05Save one VALUE per embedded RTypedDataJeremy Evans
This halves the amount of memory used for embedded RTypedData if they are one VALUE (8 bytes on 64-bit platforms) over the slot size limit. For Set, on 64-bit it uses an embedded 56-byte struct. With the previous implementation, the embedded structs starts at offset 32, resulting in a total size of 88. Since that is over the 80 byte limit, it goes to the next highest bucket, 160 bytes, wasting 72 bytes. This allows it to fit in a 80 byte bucket, which reduces the total size for small sets of from 224 bytes (160 bytes embedded, 64 bytes malloc, 72 bytes wasted in embedding) to 144 bytes (80 bytes embedded, 64 bytes malloc, 0 bytes wasted in embedding). Any other embedded RTypedData will see similar advantages if they are currently one VALUE over the limit. To implement this, remove the typed_flag from struct RTypedData. Embed the typed_flag information in the type member, which is now a tagged pointer using VALUE type, using the bottom low 2 bits as flags (1 bit for typed flag, the other for the embedded flag). To get the actual pointer, RTYPEDDATA_TYPE masks out the low 2 bits and then casts. That moves the RTypedData data pointer from offset 32 to offset 24 (on 64-bit). Vast amount of code in the internals (and probably external C extensions) expects the following code to work for both RData and non-embedded RTypedData: ```c DATA_PTR(obj) = some_pointer; ``` Allow this to work by moving the data pointer in RData between the dmark and dfree pointers, so it is at the same offset (24 on 64-bit). Other than these changes to the include files, the only changes needed were to gc.c, to account for the new struct layouts, handle setting the low bits in the type member, and to use RTYPEDDATA_TYPE(obj) instead of RTYPEDDATA(obj)->type. Notes: Merged: https://github.com/ruby/ruby/pull/13190
2025-05-02Also prefer FL_TEST_RAW in gc.cJohn Hawthorn
Similar to 4a040eeb0d880b67a5005cce382122fd5b629b99, I noticed the test for FL_FINALIZE checking FL_ABLE in a profile, and we shouldn't need to do that here. Notes: Merged: https://github.com/ruby/ruby/pull/13233
2025-04-19Fix style [ci skip]Nobuyoshi Nakada
2025-04-19Ensure `struct rb_io` is passed through to `thread.c`. (#13134)Samuel Williams
Notes: Merged-By: ioquatix <[email protected]>
2025-04-18Lock-free hash set for fstrings [Feature #21268]John Hawthorn
This implements a hash set which is wait-free for lookup and lock-free for insert (unless resizing) to use for fstring de-duplication. As highlighted in https://bugs.ruby-lang.org/issues/19288, heavy use of fstrings (frozen interned strings) can significantly reduce the parallelism of Ractors. I tried a few other approaches first: using an RWLock, striping a series of RWlocks (partitioning the hash N-ways to reduce lock contention), and putting a cache in front of it. All of these improved the situation, but were unsatisfying as all still required locks for writes (and granular locks are awkward, since we run the risk of needing to reach a vm barrier) and this table is somewhat write-heavy. My main reference for this was Cliff Click's talk on a lock free hash-table for java https://www.youtube.com/watch?v=HJ-719EGIts. It turns out this lock-free hash set is made easier to implement by a few properties: * We only need a hash set rather than a hash table (we only need keys, not values), and so the full entry can be written as a single VALUE * As a set we only need lookup/insert/delete, no update * Delete is only run inside GC so does not need to be atomic (It could be made concurrent) * I use rb_vm_barrier for the (rare) table rebuilds (It could be made concurrent) We VM lock (but don't require other threads to stop) for table rebuilds, as those are rare * The conservative garbage collector makes deferred replication easy, using a T_DATA object Another benefits of having a table specific to fstrings is that we compare by value on lookup/insert, but by identity on delete, as we only want to remove the exact string which is being freed. This is faster and provides a second way to avoid the race condition in https://bugs.ruby-lang.org/issues/21172. This is a pretty standard open-addressing hash table with quadratic probing. Similar to our existing st_table or id_table. Deletes (which happen on GC) replace existing keys with a tombstone, which is the only type of update which can occur. Tombstones are only cleared out on resize. Unlike st_table, the VALUEs are stored in the hash table itself (st_table's bins) rather than as a compact index. This avoids an extra pointer dereference and is possible because we don't need to preserve insertion order. The table targets a load factor of 2 (it is enlarged once it is half full). Notes: Merged: https://github.com/ruby/ruby/pull/12921
2025-04-18Extract rb_gc_free_fstring to string.cJohn Hawthorn
This allows more flexibility in how we deal with the fstring table Notes: Merged: https://github.com/ruby/ruby/pull/12921
2025-04-15Lazily create `objspace->id_to_obj_tbl`Jean Boussier
This inverse table is only useful if `ObjectSpace._id2ref` is used, which is extremely rare. The only notable exception is the `drb` gem and even then it has an option not to rely on `_id2ref`. So if we assume this table will never be looked up, we can just not maintain it, and if it turns out `_id2ref` is called, we can lock the VM and re-build it. ``` compare-ruby: ruby 3.5.0dev (2025-04-10T09:44:40Z master 684cfa42d7) +YJIT +PRISM [arm64-darwin24] built-ruby: ruby 3.5.0dev (2025-04-10T10:13:43Z lazy-id-to-obj d3aa9626cc) +YJIT +PRISM [arm64-darwin24] warming up.. | |compare-ruby|built-ruby| |:----------|-----------:|---------:| |baseline | 26.364M| 25.974M| | | 1.01x| -| |object_id | 10.293M| 14.202M| | | -| 1.38x| ``` Notes: Merged: https://github.com/ruby/ruby/pull/13115
2025-04-04Ractor: revert to moving object bytes, but size pool awareJean Boussier
Using `rb_obj_clone` introduce other problems, such as `initialize_*` callbacks invocation in the context of the parent ractor. So we can revert back to copy the content of the object slots, but in a way that is aware of size pools. Notes: Merged: https://github.com/ruby/ruby/pull/13070
2025-03-31Don't preserve `object_id` when moving object to another RactorJean Boussier
That seemed like the logical thing to do to me, but ko1 disagree. Notes: Merged: https://github.com/ruby/ruby/pull/13008
2025-03-31Ractor: Fix moving embedded objectsJean Boussier
[Bug #20271] [Bug #20267] [Bug #20255] `rb_obj_alloc(RBASIC_CLASS(obj))` will always allocate from the basic 40B pool, so if `obj` is larger than `40B`, we'll create a corrupted object when we later copy the shape_id. Instead we can use the same logic than ractor copy, which is to use `rb_obj_clone`, and later ask the GC to free the original object. We then must turn it into a `T_OBJECT`, because otherwise just changing its class to `RactorMoved` leaves a lot of ways to keep using the object, e.g.: ``` a = [1, 2, 3] Ractor.new{}.send(a, move: true) [].concat(a) # Should raise, but wasn't. ``` If it turns out that `rb_obj_clone` isn't performant enough for some uses, we can always have carefully crafted specialized paths for the types that would benefit from it. Notes: Merged: https://github.com/ruby/ruby/pull/13008
2025-03-26Re-use objspace variable instead of calling rb_gc_get_objspace()Peter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12979
2025-03-24Move rb_gc_impl_ractor_cache_free to shutdown sectionPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12965
2025-03-24Move rb_gc_impl_objspace_free to shutdown sectionPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12965
2025-02-19Add rb_gc_object_metadata APIPeter Zhu
This function replaces the internal rb_obj_gc_flags API. rb_gc_object_metadata returns an array of name and value pairs, with the last element having 0 for the name. Notes: Merged: https://github.com/ruby/ruby/pull/12777
2025-02-18Fix crash for special constants in too complex generic ivarsPeter Zhu
We should skip reference updating for entries in too complex generic ivars that are special constants. This fixes the following crash: MAX_SHAPES = 0x80000 MAX_SHAPES.times do |i| o = [] o.instance_variable_set(:"@foo#{i}", 1) end o = [] o.instance_variable_set(:"@a", 123) GC.compact Notes: Merged: https://github.com/ruby/ruby/pull/12781
2025-02-13[Feature #21116] Extract RJIT as a third-party gemNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/12740
2025-02-10Move global symbol reference updating to rb_sym_global_symbols_update_referencesPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12711
2025-02-10Make ruby_global_symbols movablePeter Zhu
The `ids` array and `dsymbol_fstr_hash` were pinned because they were kept alive by rb_vm_register_global_object. This prevented the GC from moving them even though there were reference updating code. This commit changes it to be marked movable by marking it as a root object. Notes: Merged: https://github.com/ruby/ruby/pull/12711
2025-01-30[Bug #21099] Fix GC when Ractor list not initializedPeter Zhu
When the Ractor list is not initialized and a GC is ran at boot, then it would crash because the newobj_cache of the main Ractor is not cleared. This commit changes it to use ruby_single_main_ractor when it's available and iterate over the Ractor list when we have multiple Ractors. Notes: Merged: https://github.com/ruby/ruby/pull/12667
2025-01-27Use rb_gc_vm_weak_table_foreach for reference updatingPeter Zhu
We can use rb_gc_vm_weak_table_foreach for reference updating of weak tables in the default GC. Notes: Merged: https://github.com/ruby/ruby/pull/12629
2025-01-27Optionally traverse non-weak references in rb_gc_vm_weak_table_foreachPeter Zhu
For moving garbage collectors, we may want to combine liveliness checking with reference updating for performance. This commit allows for non-weak references to be passed into the callback function when weak_only is false. Notes: Merged: https://github.com/ruby/ruby/pull/12629
2025-01-23Fix memory leak in rb_gc_vm_weak_table_foreachPeter Zhu
When deleting from the generic ivar table, we need to free the gen_ivtbl otherwise we will have a memory leak. Notes: Merged: https://github.com/ruby/ruby/pull/12615
2025-01-22Add generic ivar reference updating stepPeter Zhu
Previously, generic ivars worked differently than the other global tables during compaction. The other global tables had their references updated through iteration during rb_gc_update_vm_references. Generic ivars updated the keys when the object moved and updated the values while reference updating the object. This is inefficient as this required one lookup for every moved object and one lookup for every object with generic ivars. Instead, this commit changes it to iterate over the generic ivar table to update both the keys and values. Notes: Merged: https://github.com/ruby/ruby/pull/12607
2025-01-16Add not null checks to rb_gc_vm_weak_table_foreachPeter Zhu
If the tables are null (which happens when a GC is ran at boot), it will segfault when trying to iterate. Notes: Merged: https://github.com/ruby/ruby/pull/12591
2025-01-16Un-constify `mark_current_machine_context` on wasmNobuyoshi Nakada
As `SET_STACK_END` updates `ec->machine.stack_end`, it cannot be const.
2025-01-15Use existing vm variable for frozen strings in rb_gc_vm_weak_table_foreachPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12582
2025-01-02Move rbimpl_size_add_overflow from gc.c to memory.hPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12459
2024-12-25[Bug #20981] Bring back `rb_undefine_finalizer`Nobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/12468
2024-12-23Disable GC during RUBY_INTERNAL_EVENT_NEWOBJPeter Zhu
We must disable GC when running RUBY_INTERNAL_EVENT_NEWOBJ hooks because the callback could call xmalloc which could potentially trigger a GC, and a lot of code is unsafe to trigger a GC right after an object has been allocated because they perform initialization for the object and assume that the GC does not trigger before then. Notes: Merged: https://github.com/ruby/ruby/pull/12419
2024-12-22Allow variables in modular_gc_dirNobuyoshi Nakada
Such as `$(ruby_version)`, `$(arch)` and so on. Notes: Merged: https://github.com/ruby/ruby/pull/12428
2024-12-22Support RUBY_MODULAR_GC with LOAD_RELATIVENobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/12428
2024-12-20Don't print bug report in asan_death_callback when no VMPeter Zhu
If we don't have the VM (e.g. printing memory leaks in LSAN after shutdown) then we will crash when we try to print the bug report. This issue was reported in: https://github.com/ruby/ruby/pull/12309#issuecomment-2555766525 Notes: Merged: https://github.com/ruby/ruby/pull/12413
2024-12-20Memerror is fatal if VM cannot be unlocked.Matt Valentine-House
[Bug #20942] If we've raised a memerror while the VM is locked, and the tag we're jumping to has been locked at a different level to the current lock (ie. we've locked the VM again since the tag we're jumping to) then we should consider this memerror fatal and exit, since the tag cannot unlock the VM. Co-Authored-By: Peter Zhu <[email protected]> Notes: Merged: https://github.com/ruby/ruby/pull/12393
2024-12-19Prefix asan_poison_object with rbPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12385
2024-12-19Make asan_poison_object poison the whole slotPeter Zhu
This change poisons the whole slot of the object rather than just the flags. This allows ASAN to find any reads/writes into the slot after it has been freed. Notes: Merged: https://github.com/ruby/ruby/pull/12385
2024-12-17Assert Ruby object in rb_gc_locationPeter Zhu
rb_gc_location doesn't check that the object is actually a Ruby object and only checks if the object looks like a T_MOVED. This may have unexpected outcomes if the object is not a Ruby object (e.g. a piece of malloc memory may be corrupted). Notes: Merged: https://github.com/ruby/ruby/pull/12371
2024-12-16Don't directly use rb_gc_impl_location in gc.cPeter Zhu
Use the wrapper gc_location_internal instead that checks for special constants. Notes: Merged: https://github.com/ruby/ruby/pull/12359
2024-12-16Move special constant check in rb_gc_location to gc.cPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12359
2024-12-16Check whether object is valid in allocation_info_tracer_compactPeter Zhu
When reference updating ObjectSpace.trace_object_allocations, we need to check whether the object is valid or not because it does not mark the object so the object may be dead. This can cause a segmentation fault if the object is on a free heap page. For example, the following script crashes: require "objspace" objs = [] ObjectSpace.trace_object_allocations do 1_000_000.times do objs << Object.new end end objs = nil # Free pages that the objs were on GC.start # Run compaction and check that it doesn't crash GC.compact Notes: Merged: https://github.com/ruby/ruby/pull/12360
2024-12-12Call rb_bug_without_die when ASAN error reportedPeter Zhu
This will give us the Ruby stack trace when an ASAN error is reported. Notes: Merged: https://github.com/ruby/ruby/pull/12309
2024-12-11[Bug #20941] Bail out when recursing no memoryNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/12307
2024-12-06Add rb_gc_impl_active_gc_name to gc/gc_impl.hPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/12271
2024-12-05Standardize on the name "modular GC"Peter Zhu
We have name fragmentation for this feature, including "shared GC", "modular GC", and "external GC". This commit standardizes the feature name to "modular GC" and the implementation to "GC library". Notes: Merged: https://github.com/ruby/ruby/pull/12261
2024-12-04Use BUILDING_SHARED_GC instead of RB_AMALGAMATED_DEFAULT_GCPeter Zhu
We can use the BUILDING_SHARED_GC flag to check if we're building gc_impl.h as a shared GC or building the default GC. Notes: Merged: https://github.com/ruby/ruby/pull/12243