mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Prevent ObjectSpace.count_objects from allocating extra arrays
`ObjectSpace.count_objects` could cause an unintended array allocation.
It returns a hash like `{ :T_ARRAY => 100, :T_STRING => 100, ... }`, so
it creates the key symbol (e.g., `:T_STRING`) for the first time. On
rare occations, this symbol creation internally allocates a new array
for symbol management.
This led to a problematic side effect where calling `count_objects`
twice in a row could produce inconsistent results: the first call would
trigger the hidden array allocation, and the second call would then
report an increased count for `:T_ARRAY`.
This behavior caused test failures in `test/ruby/test_allocation.rb`,
which performs a baseline measurement before an operation and then
asserts the exact number of new allocations.
20250716
T053005Z.fail.html.gz
> 1) Failure:
> TestAllocation::ProcCall::WithBlock#test_ruby2_keywords [...]:
> Expected 1 array allocations for "r2k.(1, a: 2, &block)", but 2 arrays allocated.
This change resolves the issue by pre-interning all key symbols used by
`ObjectSpace.count_objects` before its counting. This eliminates the
side effect and ensures the stability of allocation-sensitive tests.
Co-authored-by: Koichi Sasada <ko1@atdot.net>
This commit is contained in:
parent
3956308aa4
commit
6d17a3e647
1 changed files with 8 additions and 3 deletions
11
gc.c
11
gc.c
|
@ -2498,6 +2498,7 @@ count_objects(int argc, VALUE *argv, VALUE os)
|
||||||
{
|
{
|
||||||
struct count_objects_data data = { 0 };
|
struct count_objects_data data = { 0 };
|
||||||
VALUE hash = Qnil;
|
VALUE hash = Qnil;
|
||||||
|
VALUE types[T_MASK + 1];
|
||||||
|
|
||||||
if (rb_check_arity(argc, 0, 1) == 1) {
|
if (rb_check_arity(argc, 0, 1) == 1) {
|
||||||
hash = argv[0];
|
hash = argv[0];
|
||||||
|
@ -2505,6 +2506,10 @@ count_objects(int argc, VALUE *argv, VALUE os)
|
||||||
rb_raise(rb_eTypeError, "non-hash given");
|
rb_raise(rb_eTypeError, "non-hash given");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i <= T_MASK; i++) {
|
||||||
|
types[i] = type_sym(i);
|
||||||
|
}
|
||||||
|
|
||||||
rb_gc_impl_each_object(rb_gc_get_objspace(), count_objects_i, &data);
|
rb_gc_impl_each_object(rb_gc_get_objspace(), count_objects_i, &data);
|
||||||
|
|
||||||
if (NIL_P(hash)) {
|
if (NIL_P(hash)) {
|
||||||
|
@ -2517,9 +2522,9 @@ count_objects(int argc, VALUE *argv, VALUE os)
|
||||||
rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(data.freed));
|
rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(data.freed));
|
||||||
|
|
||||||
for (size_t i = 0; i <= T_MASK; i++) {
|
for (size_t i = 0; i <= T_MASK; i++) {
|
||||||
VALUE type = type_sym(i);
|
if (data.counts[i]) {
|
||||||
if (data.counts[i])
|
rb_hash_aset(hash, types[i], SIZET2NUM(data.counts[i]));
|
||||||
rb_hash_aset(hash, type, SIZET2NUM(data.counts[i]));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue