[pull] master from ruby:master#920
Merged
pull[bot] merged 34 commits intoturkdevops:masterfrom Apr 9, 2026
Merged
Conversation
There is only two rare cases where they need to be sweeped:
- If they are so large that they're heap allocated.
- If they have an object_id (and id2ref_tbl exist but
we can't check that here)
When we allocate a RHash using `rb_hash_new_capa()`, if `capa` is
larger than `8` it's directly allocated as an `st_stable` in a `80B`
slot.
However if the requested size if lesser or equal to 8, we allocate
it as an `ar_table` in a `160B` slot.
Since most hashes are allocated as mutable, we have to be able to
accomodate as much as 8 AR_TABLE entries regardless.
However there are case where we know the Hash won't ever be resized,
that notably the case of all the "literal" hashes allocated by the
compiler.
These are immediately frozen and hidden upon being constructed, hence
we can know for sure they won't ever be resized. This allows us to
allocate the smaller ones in smaller slots.
```
size: 0, slot_size: 32
size: 1, slot_size: 48
size: 2, slot_size: 64
size: 3, slot_size: 80
size: 4, slot_size: 96
size: 5, slot_size: 112
size: 6, slot_size: 128
size: 7, slot_size: 144
size: 8, slot_size: 160
```
```ruby
require "objspace"
p ObjectSpace.memsize_of({}.freeze) # => 40
p ObjectSpace.memsize_of({a: 1}.freeze) # => 80
p ObjectSpace.memsize_of({a: 1, b: 2}.freeze) # => 80
p ObjectSpace.memsize_of({a: 1, b: 2, c: 3}.freeze) # => 80
p ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4}.freeze) # => 160
p ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4, e: 5, }.freeze) # => 160
p ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}.freeze) # => 160
p ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7}.freeze) # => 160
p ObjectSpace.memsize_of({a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8}.freeze) # => 160
```
This otherwise causes a crash on e.g. #15220: thread '<unnamed>' (18071) panicked at zjit/src/codegen.rs:2511:21: internal error: entered unreachable code: with_num_bits should not be used for: Value(VALUE(19239180)) stack backtrace: 0: __rustc::rust_begin_unwind at /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library/std/src/panicking.rs:689:5 1: core::panicking::panic_fmt at /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library/core/src/panicking.rs:80:14 2: zjit::backend::lir::Opnd::with_num_bits at /home/runner/work/ruby/ruby/src/zjit/src/backend/lir.rs:457:18 3: zjit::codegen::gen_guard_type at /home/runner/work/ruby/ruby/src/zjit/src/codegen.rs:2511:21 4: zjit::codegen::gen_insn at /home/runner/work/ruby/ruby/src/zjit/src/codegen.rs:690:55
ArrayPush calls out to the fast-path, not checking for frozen-ness. In debug mode, this leads to crashes. In release mode, silent erroneous modifications.
Bumps the github-actions group with 1 update in the / directory: [taiki-e/install-action](https://github.com/taiki-e/install-action). Updates `taiki-e/install-action` from 2.75.0 to 2.75.1 - [Release notes](https://github.com/taiki-e/install-action/releases) - [Changelog](https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md) - [Commits](taiki-e/install-action@cf39a74...80e6af7) --- updated-dependencies: - dependency-name: taiki-e/install-action dependency-version: 2.75.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com>
Apple clang aborts if cpp output is closed in middle, and leaves the preprocessed source and reproduction shell script.
When checking for suffixes, do not flush the numeric literal token even if no suffix is found.
Wait for terminated threads to finish.
It may be set to "false" if usable compiler is not found.
Same handling as for `yield`. Fixes [Bug #21988] ruby/prism@2dd20183ad
With smaller pool sizes (e.g. 32 bytes), an RObject with 0 embedded fields would request a size smaller than the minimum meaningful object. Ensure at least 1 field worth of space so the embedded size is always large enough for a valid RObject.
When pool slot sizes can be smaller than sizeof(struct RBasic) (e.g. a 32-byte pool on 64-bit where RBasic is 16 bytes), the capacity calculation would underflow. Guard against this by setting capacity to 0 for pools too small to hold fields.
Replace the RVALUE_SLOT_SIZE-multiplier based pool sizes with explicit power-of-two (and near-power-of-two) slot sizes. On 64-bit this gives 12 heaps (32, 40, 64, 80, 96, 128, 160, 256, 512, 640, 768, 1024) instead of 5, providing finer granularity and less internal fragmentation. On 32-bit the layout is 5 heaps (32, 64, 128, 256, 512).
Add GC::INTERNAL_CONSTANTS[:RVALUE_SIZE] to store the usable size (excluding debug overhead) of the smallest pool that can hold a standard RVALUE.
Add a 7/8 multiplier to the min_free_slots checks in gc_sweep_finish_heap and gc_marks_finish, allowing heaps to be up to ~12.5% below the free slots target without triggering a major GC or forced growth. With 12 heaps instead of 5, each heap independently hitting the exact threshold would cause excessive memory growth. The slack prevents cascading growth decisions while still ensuring heaps stay close to their target occupancy.
We implemented some bit twiddling logic with an unsigned int to have a neat way of tracking which heaps were currently sweeping, but we actually don't need to care which heap is sweeping right now, just whether some are or not, so we can replace this with a counter.
This is because when `RVALUE_OVERHEAD` is positive, ie. when `RACTOR_CHECK_MODE` is enabled and we need to store the pointer to the owning ractor, we need to make sure there is enough space to store it. With the previous size pools the smallest size pool was 40 bytes, this gets expanded to 48 bytes when debug mode is on in order to make space for this extra pointer. because rb_obj_embedded_size(0) returns just the header with no field space it wants to be allocated in the 40 byte slot, this gives 16 bytes which is enough for RBasic only, but because this slot is 48 bytes in debug mode, we get the extra space for the pointer. When the smallest slot is 32 bytes it becomes 40 bytes in debug mode, this causes objects with no ivars to to get allocated in this pool because according to this calc it fits. but this doesn't leave the extra word for the ractor pointer. So in debug mode, we'll clamp this to 1 so that there's always enough space for 1 extra field to force allocation into the 40/48 byte pool.
This isn't a 0 terminated list anymore because we iterate over heaps_count directly. So we don't need to allocate an extra byte for the sentinel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )