Skip to content

Commit 0229f80

Browse files
mameko1
andcommitted
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. https://rubyci.s3.amazonaws.com/openbsd-current/ruby-master/log/20250716T053005Z.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>
1 parent 3956308 commit 0229f80

File tree

1 file changed

+8
-3
lines changed

1 file changed

+8
-3
lines changed

gc.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,13 +2498,18 @@ count_objects(int argc, VALUE *argv, VALUE os)
24982498
{
24992499
struct count_objects_data data = { 0 };
25002500
VALUE hash = Qnil;
2501+
VALUE types[T_MASK + 1];
25012502

25022503
if (rb_check_arity(argc, 0, 1) == 1) {
25032504
hash = argv[0];
25042505
if (!RB_TYPE_P(hash, T_HASH))
25052506
rb_raise(rb_eTypeError, "non-hash given");
25062507
}
25072508

2509+
for (size_t i = 0; i <= T_MASK; i++) {
2510+
types[i] = type_sym(i);
2511+
}
2512+
25082513
rb_gc_impl_each_object(rb_gc_get_objspace(), count_objects_i, &data);
25092514

25102515
if (NIL_P(hash)) {
@@ -2517,9 +2522,9 @@ count_objects(int argc, VALUE *argv, VALUE os)
25172522
rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(data.freed));
25182523

25192524
for (size_t i = 0; i <= T_MASK; i++) {
2520-
VALUE type = type_sym(i);
2521-
if (data.counts[i])
2522-
rb_hash_aset(hash, type, SIZET2NUM(data.counts[i]));
2525+
if (data.counts[i]) {
2526+
rb_hash_aset(hash, types[i], SIZET2NUM(data.counts[i]));
2527+
}
25232528
}
25242529

25252530
return hash;

0 commit comments

Comments
 (0)