summaryrefslogtreecommitdiff
path: root/gc/gc.h
AgeCommit message (Collapse)Author
12 daysAdd rb_gc_print_backtracePeter Zhu
2025-12-29Add rb_gc_move_obj_during_markingPeter Zhu
2025-12-25Implement declaring weak referencesPeter Zhu
[Feature #21084] # Summary The current way of marking weak references uses `rb_gc_mark_weak(VALUE *ptr)`. This presents challenges because Ruby's GC is incremental, meaning that if the `ptr` changes (e.g. realloc'd or free'd), then we could have an invalid memory access. This also overwrites `*ptr = Qundef` if `*ptr` is dead, which prevents any cleanup to be run (e.g. freeing memory or deleting entries from hash tables). This ticket proposes `rb_gc_declare_weak_references` which declares that an object has weak references and calls a cleanup function after marking, allowing the object to clean up any memory for dead objects. # Introduction In [[Feature #19783]](https://bugs.ruby-lang.org/issues/19783), I introduced an API allowing objects to mark weak references, the function signature looks like this: ```c void rb_gc_mark_weak(VALUE *ptr); ``` `rb_gc_mark_weak` is called during the marking phase of the GC to specify that the memory at `ptr` holds a pointer to a Ruby object that is weakly referenced. `rb_gc_mark_weak` appends this pointer to a list that is processed after the marking phase of the GC. If the object at `*ptr` is no longer alive, then it overwrites the object reference with a special value (`*ptr = Qundef`). However, this API resulted in two challenges: 1. Ruby's default GC is incremental, which means that the GC is not ran in one phase, but rather split into chunks of work that interleaves with Ruby execution. The `ptr` passed into `rb_gc_mark_weak` could be on the malloc heap, and that memory could be realloc'd or even free'd. We had to use workarounds such as `rb_gc_remove_weak` to ensure that there were no illegal memory accesses. This made `rb_gc_mark_weak` difficult to use, impacted runtime performance, and increased memory usage. 2. When an object dies, `rb_gc_mark_weak` only overwites the reference with `Qundef`. This means that if we want to do any cleanup (e.g. free a piece of memory or delete a hash table entry), we could not do that and had to defer this process elsewhere (e.g. during marking or runtime). In this ticket, I'm proposing a new API for weak references. Instead of an object marking its weak references during the marking phase, the object declares that it has weak references using the `rb_gc_declare_weak_references` function. This declaration occurs during runtime (e.g. after the object has been created) rather than during GC. After an object declares that it has weak references, it will have its callback function called after marking as long as that object is alive. This callback function can then call a special function `rb_gc_handle_weak_references_alive_p` to determine whether its references are alive. This will allow the callback function to do whatever it wants on the object, allowing it to perform any cleanup work it needs. This significantly simplifies the code for `ObjectSpace::WeakMap` and `ObjectSpace::WeakKeyMap` because it no longer needs to have the workarounds for the limitations of `rb_gc_mark_weak`. # Performance The performance results below demonstrate that `ObjectSpace::WeakMap#[]=` is now about 60% faster because the implementation has been simplified and the number of allocations has been reduced. We can see that there is not a significant impact on the performance of `ObjectSpace::WeakMap#[]`. Base: ``` ObjectSpace::WeakMap#[]= 4.620M (± 6.4%) i/s (216.44 ns/i) - 23.342M in 5.072149s ObjectSpace::WeakMap#[] 30.967M (± 1.9%) i/s (32.29 ns/i) - 154.998M in 5.007157s ``` Branch: ``` ObjectSpace::WeakMap#[]= 7.336M (± 2.8%) i/s (136.31 ns/i) - 36.755M in 5.013983s ObjectSpace::WeakMap#[] 30.902M (± 5.4%) i/s (32.36 ns/i) - 155.901M in 5.064060s ``` Code: ``` require "bundler/inline" gemfile do source "https://rubygems.org" gem "benchmark-ips" end wmap = ObjectSpace::WeakMap.new key = Object.new val = Object.new wmap[key] = val Benchmark.ips do |x| x.report("ObjectSpace::WeakMap#[]=") do |times| i = 0 while i < times wmap[Object.new] = Object.new i += 1 end end x.report("ObjectSpace::WeakMap#[]") do |times| i = 0 while i < times wmap[key] wmap[val] # does not exist i += 1 end end end ``` # Alternative designs Currently, `rb_gc_declare_weak_references` is designed to be an internal-only API. This allows us to assume the object types that call `rb_gc_declare_weak_references`. In the future, if we want to open up this API to third parties, we may want to change this function to something like: ```c void rb_gc_add_cleaner(VALUE obj, void (*callback)(VALUE obj)); ``` This will allow the third party to implement a custom `callback` that gets called after the marking phase of GC to clean up any dead references. I chose not to implement this design because it is less efficient as we would need to store a mapping from `obj` to `callback`, which requires extra memory.
2025-11-08Move rb_gc_verify_shareable to gc.cPeter Zhu
rb_gc_verify_shareable is not GC implementation specific so it should live in gc.c.
2025-10-23catch up modular-gcKoichi Sasada
2025-09-02Output parent object info when marking T_NONEPeter Zhu
2025-07-14YJIT: Set code mem permissions in bulkKunshan Wang
Some GC modules, notably MMTk, support parallel GC, i.e. multiple GC threads work in parallel during a GC. Currently, when two GC threads scan two iseq objects simultaneously when YJIT is enabled, both threads will attempt to borrow `CodeBlock::mem_block`, which will result in panic. This commit makes one part of the change. We now set the YJIT code memory to writable in bulk before the reference-updating phase, and reset it to executable in bulk after the reference-updating phase. Previously, YJIT lazily sets memory pages writable while updating object references embedded in JIT-compiled machine code, and sets the memory back to executable by calling `mark_all_executable`. This approach is inherently unfriendly to parallel GC because (1) it borrows `CodeBlock::mem_block`, and (2) it sets the whole `CodeBlock` as executable which races with other GC threads that are updating other iseq objects. It also has performance overhead due to the frequent invocation of system calls. We now set the permission of all the code memory in bulk before and after the reference updating phase. Multiple GC threads can now perform raw memory writes in parallel. We should also see performance improvement during moving GC because of the reduced number of `mprotect` system calls.
2025-06-26Support message in GC_ASSERTPeter Zhu
RUBY_ASSERT_MESG_WHEN supports a format string at the end for additional information. This commit adds support for that in GC_ASSERT.
2025-06-09Take file and line in GC VM locksPeter Zhu
This commit adds file and line to GC VM locking functions for debugging purposes and adds upper case macros to pass __FILE__ and __LINE__. Notes: Merged: https://github.com/ruby/ruby/pull/13550
2025-06-09Optimize callcache invalidation for refinementsalpaca-tc
Fixes [Bug #21201] This change addresses a performance regression where defining methods inside `refine` blocks caused severe slowdowns. The issue was due to `rb_clear_all_refinement_method_cache()` triggering a full object space scan via `rb_objspace_each_objects` to find and invalidate affected callcaches, which is very inefficient. To fix this, I introduce `vm->cc_refinement_table` to track callcaches related to refinements. This allows us to invalidate only the necessary callcaches without scanning the entire heap, resulting in significant performance improvement. Notes: Merged: https://github.com/ruby/ruby/pull/13077
2025-06-06Introduce MODULAR_GC_FNPeter Zhu
MODULAR_GC_FN allows functions in gc.c to be defined as static when not building with modular GC. Notes: Merged: https://github.com/ruby/ruby/pull/13539
2025-05-14Rename `id_to_obj_tbl` -> `id2ref_tbl`Jean Boussier
As well as associated functions, this should make it more obvious what the purpose is. Notes: Merged: https://github.com/ruby/ruby/pull/13334
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 <matt@eightbitraptor.com> 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-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
2024-12-11[Bug #20941] Bail out when recursing no memoryNobuyoshi Nakada
Notes: Merged: https://github.com/ruby/ruby/pull/12307
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-11-25Place all non-default GC API behind USE_SHARED_GCMatt Valentine-House
So that it doesn't get included in the generated binaries for builds that don't support loading shared GC modules Co-Authored-By: Peter Zhu <peter@peterzhu.ca> Notes: Merged: https://github.com/ruby/ruby/pull/12149
2024-11-25Use extconf to build external GC modulesMatt Valentine-House
Co-Authored-By: Peter Zhu <peter@peterzhu.ca> Notes: Merged: https://github.com/ruby/ruby/pull/12149
2024-10-18Move object processing in Process.warmup to gc.cPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11904
2024-10-07Disable -Wunused-function for shared GC in gc/gc.hPeter Zhu
Shared GC might not use the private functions in gc/gc.h, so they will show up as warnings for unused functions. This commit disables -Wunused-function for these functions when building as shared GC. Notes: Merged: https://github.com/ruby/ruby/pull/11808
2024-10-03Rename size_pool -> heapMatt Valentine-House
Now that we've inlined the eden_heap into the size_pool, we should rename the size_pool to heap. So that Ruby contains multiple heaps, with different sized objects. The term heap as a collection of memory pages is more in memory management nomenclature, whereas size_pool was a name chosen out of necessity during the development of the Variable Width Allocation features of Ruby. The concept of size pools was introduced in order to facilitate different sized objects (other than the default 40 bytes). They wrapped the eden heap and the tomb heap, and some related state, and provided a reasonably simple way of duplicating all related concerns, to provide multiple pools that all shared the same structure but held different objects. Since then various changes have happend in Ruby's memory layout: * The concept of tomb heaps has been replaced by a global free pages list, with each page having it's slot size reconfigured at the point when it is resurrected * the eden heap has been inlined into the size pool itself, so that now the size pool directly controls the free_pages list, the sweeping page, the compaction cursor and the other state that was previously being managed by the eden heap. Now that there is no need for a heap wrapper, we should refer to the collection of pages containing Ruby objects as a heap again rather than a size pool Notes: Merged: https://github.com/ruby/ruby/pull/11771
2024-10-02Deduplicate RGENGC_CHECK_MODE into gc/gc.hPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11764
2024-09-03Move responsibility of heap walking into RubyPeter Zhu
This commit removes the need for the GC implementation to implement heap walking and instead Ruby will implement it. Notes: Merged: https://github.com/ruby/ruby/pull/11511
2024-08-22Don't use gc_impl.h inside of gc/gc.hPeter Zhu
Using gc_impl.h inside of gc/gc.h will cause gc/gc.h to use the functions in gc/default.c when builing with shared GC support because gc/gc.h is included into gc.c before the rb_gc_impl functions are overridden by the preprocessor. Notes: Merged: https://github.com/ruby/ruby/pull/11423
2024-08-22Change hash_replace_ref_value to assume value movedPeter Zhu
When hash_foreach_replace_value returns ST_REPLACE, it's guaranteed that the value has moved in hash_replace_ref_value. Notes: Merged: https://github.com/ruby/ruby/pull/11423
2024-08-15Fix GC_ASSERT for gc.c and gc/default.cPeter Zhu
gc.c mistakenly defined GC_ASSERT as blank, which caused it to be a no-op. This caused all assertions in gc.c and gc/default.c to not do anything. This commit fixes it by moving the definition of GC_ASSERT to gc/gc.h. Notes: Merged: https://github.com/ruby/ruby/pull/11377
2024-07-26Put the default GC implementation back into gc.oAlan Wu
We discovered that having gc.o and gc_impl.o in separate translation units diminishes codegen quality with GCC 11 on x86-64. This commit solves that problem by including default/gc.c into gc.c, letting the optimizer have visibility into the body of functions again in builds not using link-time optimization, which are common. This effectively restores things to the way they were before [Feature #20470] from the optimizer's perspective while maintaining the ability to build gc/default.c as a DSO. There were a few functions duplicated across gc.c and gc/default.c. Extract them and put them into gc/gc.h.
2024-07-15Add gc/gc.h for functions in gc.c and used by GC implementationsPeter Zhu