These classes don't belong in gc.c as they're not actually part of the
GC. This commit refactors the code by moving all the code into a
weakmap.c file.
This makes the behavior of classes and modules when there are too many instance variables match the behavior of objects with too many instance variables.
When a Ractor is created whilst a tracepoint for
RUBY_INTERNAL_EVENT_NEWOBJ is active, the interpreter crashes. This is
because during the early setup of the Ractor, the stdio objects are
created, which allocates Ruby objects, which fires the tracepoint.
However, the tracepoint machinery tries to dereference the control frame
(ec->cfp->pc), which isn't set up yet and so crashes with a null pointer
dereference.
Fix this by not firing GC tracepoints if cfp isn't yet set up.
We need to zero out the whole slot when running the newobj hook for a
newly allocated class because the slot could be filled with garbage,
which would cause a crash if a GC runs inside of the newobj hook.
For example, the following script crashes:
```
require "objspace"
GC.stress = true
ObjectSpace.trace_object_allocations {
100.times do
Class.new
end
}
```
[Bug #19482]
This commit adds a function rb_data_free used by obj_free and
rb_objspace_call_finalizer to free T_DATA objects. This change also
means that RUBY_TYPED_FREE_IMMEDIATELY objects can be freed immediately
in rb_objspace_call_finalizer rather than being created into a zombie.
This feature was introduced in commit 2ccf6e5, but I realized that
using rb_warn is a bad idea because it allocates objects, which causes
a different crash ("object allocation during garbage collection phase").
We should just hard crash here instead.
If the previous instruction is not a leaf instruction, then the PC was
incremented before the instruction was ran (meaning the currently
executing instruction is actually the previous instruction), so we
should not increment the PC otherwise we will calculate the source
line for the next instruction.
This bug can be reproduced in the following script:
```
require "objspace"
ObjectSpace.trace_object_allocations_start
a =
1.0 / 0.0
p [ObjectSpace.allocation_sourceline(a), ObjectSpace.allocation_sourcefile(a)]
```
Which outputs: [4, "test.rb"]
This is incorrect because the object was allocated on line 10 and not
line 4. The behaviour is correct when we use a leaf instruction (e.g.
if we replaced `1.0 / 0.0` with `"hello"`), then the output is:
[10, "test.rb"].
[Bug #19456]
We shouldn't run gc_verify_internal_consistency after every GC step
when RGENGC_CHECK_MODE >= 2, only when GC has finished. Running it
on every GC step makes it too slow.
There is a `time` key in GC.stat that gives us the total time spent in
GC. However, we don't know what proportion of the time is spent between
marking and sweeping. This makes it difficult to tune the GC as we're
not sure where to focus our efforts on.
This PR adds keys `marking_time` and `sweeping_time` to GC.stat for the
time spent marking and sweeping, in milliseconds.
[Feature #19437]
This macro is broken when set to anything other than 0. And has had a
comment saying that it's broken for 3 years.
This commit deletes it and the associated logging code. It's clearly
not being used.
Co-Authored-By: Peter Zhu <peter@peterzhu.ca>
Given that signleton classes don't have an allocator,
we can re-use these bytes to store the attached object
in `rb_classext_struct` without making it larger.
The old RUBY_GC_HEAP_INIT_SLOTS isn't really usable anymore as
it initalize all the pools by the same factor, but it's unlikely
that pools will need similar sizes.
In production our 40B pool is 5 to 6 times bigger than our 80B pool.
It's not uncommon for simple binding to wrap structs without
any Ruby object references. Hence with no `mark` function.
Might as well mark them as protected by a write barrier.
There's a memory leak in ObjectSpace::WeakMap due to not freeing
the `struct weakmap`. It can be seen in the following script:
```
100.times do
10000.times do
ObjectSpace::WeakMap.new
end
# Output the Resident Set Size (memory usage, in KB) of the current Ruby process
puts `ps -o rss= -p #{$$}`
end
```
Instance variables held in gen_ivtbl are marked with rb_gc_mark. It
prevents the referenced objects from moving, which is bad for copying
garbage collectors.
This commit allows those instance variables to be updated during
gc_update_object_references.
This commit adds rb_gc_mark_and_move which takes a pointer to an object
and marks it during marking phase and updates references during compaction.
This allows for marking and reference updating to be combined into a
single function, which reduces code duplication and prevents bugs if
marking and reference updating goes out of sync.
This commit also implements rb_gc_mark_and_move on iseq as an example.
This commit moves the classpath (and tmp_classpath) from instance
variables to the rb_classext_t. This improves performance as we no
longer need to set an instance variable when assigning a classpath to
a class.
I benchmarked with the following script:
```ruby
name = :MyClass
puts(Benchmark.measure do
10_000_000.times do |i|
Object.const_set(name, Class.new)
Object.send(:remove_const, name)
end
end)
```
Before this patch:
```
5.440119 0.025264 5.465383 ( 5.467105)
```
After this patch:
```
4.889646 0.028325 4.917971 ( 4.942678)
```
The reference updating code for strings is not re-embedding strings
because the code is incorrectly wrapped inside of a
`if (STR_SHARED_P(obj))` clause. Shared strings can't be re-embedded
so this ends up being a no-op. This means that strings can be moved to a
large size pool during compaction, but won't be re-embedded, which would
waste the space.
There is an integer underflow when the environment variable
RUBY_GC_HEAP_INIT_SLOTS is less than the number of slots currently
in the Ruby heap.
[Bug #19284]
If a size pooll is small, then `min_free_slots < heap_init_slots` is true.
This means that min_free_slots will be set to heap_init_slots. This
causes `swept_slots < min_free_slots` to be true in a later if statement.
The if statement could trigger a major GC which could cause major GC
thrashing.
gc_compact_move incorrectly returns false when destination heap is full
after sweeping. It returns false even if destination heap is different
than source heap (returning false means that the source heap has
finished compacting). This causes the source page to get locked, which
causes a read barrier fire when we try to compact the source heap again.
We eagerly set the new shape of an object when moving an object during
compaction. This new shape may have a different capacity than the
current original shape capacity. This means that we cannot copy from the
original buffer using size of the new capacity. Instead, we should use
the ivar count (which is less than or equal to both the new and original
capacities).
Co-Authored-By: Matt Valentine-House <matt@eightbitraptor.com>