This `st_table` is used to both mark and pin classes
defined from the C API. But `vm->mark_object_ary` already
does both much more efficiently.
Currently a Ruby process starts with 252 rooted classes,
which uses `7224B` in an `st_table` or `2016B` in an `RArray`.
So a baseline of 5kB saved, but since `mark_object_ary` is
preallocated with `1024` slots but only use `405` of them,
it's a net `7kB` save.
`vm->mark_object_ary` is also being refactored.
Prior to this changes, `mark_object_ary` was a regular `RArray`, but
since this allows for references to be moved, it was marked a second
time from `rb_vm_mark()` to pin these objects.
This has the detrimental effect of marking these references on every
minors even though it's a mostly append only list.
But using a custom TypedData we can save from having to mark
all the references on minor GC runs.
Addtionally, immediate values are now ignored and not appended
to `vm->mark_object_ary` as it's just wasted space.
This frees FL_USER0 on both T_MODULE and T_CLASS.
Note: prior to this, FL_SINGLETON was never set on T_MODULE,
so checking for `FL_SINGLETON` without first checking that
`FL_TYPE` was `T_CLASS` was valid. That's no longer the case.
is_markable_object is called by rb_objspace_markable_object_p, which
may pass a T_NONE object. check_rvalue_consistency will fail if a T_NONE
object is passed in.
The name is misleading, as it seems like the function checks whether the
object is swept or not. But the function only checks whether the page is
before or after sweeping.
The FL_FINALIZE flag is set when there are finalizers for the object. We
can improver performance by not looking up in the table if the flag is
not set.
Using the following C extension:
#include "ruby/ruby.h"
static void data_free(void *_ptr) {}
static const rb_data_type_t data_type = {
"my_type",
{
NULL,
data_free,
},
0, 0, 0
};
static VALUE data_alloc(VALUE klass) {
return TypedData_Wrap_Struct(klass, &data_type, (void *)1);
}
void Init_myext(void) {
VALUE my_klass = rb_define_class("MyClass", rb_cObject);
rb_define_alloc_func(my_klass, data_alloc);
}
And the following benchmark:
require "benchmark"
final_objs = 1_000_000.times.map do
o = Object.new
ObjectSpace.define_finalizer(o, proc {})
o
end
puts(Benchmark.measure do
100_000_000.times do
MyClass.new
end
end)
Before:
10.974190 0.355037 11.329227 ( 11.416772)
After:
7.664310 0.347598 8.011908 ( 8.268969)
Rather than exposing that an imemo has a flag and four fields, this
changes the implementation to only expose one field (the klass) and
fills the rest with 0. The type will have to fill in the values themselves.
The switch statement is not exhaustive, meaning the "unreachable"
comment was not correct. This commit fixes it by making the list
exhaustive and adding an rb_bug in the default case.
Previously every call to vm_ci_new (when the CI was not packable) would
result in a different callinfo being returned this meant that every
kwarg callsite had its own CI.
When calling, different CIs result in different CCs. These CIs and CCs
both end up persisted on the T_CLASS inside cc_tbl. So in an eval loop
this resulted in a memory leak of both types of object. This also likely
resulted in extra memory used, and extra time searching, in non-eval
cases.
For simplicity in this commit I always allocate a CI object inside
rb_vm_ci_lookup, but ideally we would lazily allocate it only when
needed. I hope to do that as a follow up in the future.
rb_objspace_call_finalizer didn't free fibers and neither did
rb_objspace_free_objects, which caused fibers to be reported as leaked
when using RUBY_FREE_AT_EXIT. This commit changes rb_objspace_free_objects
to free all remaining Ruby objects.