Currently, when calling instance_eval against an object, JRuby will eagerly create a singleton class in preparation for any method definitions that might be coming. CRuby, on the other hand, defers creation until methods actually need to be defined, making instance_eval much cheaper in the absence of defs.
We can see this effect by doing instance_eval against chilled strings, which triggers verbose warnings under 3.4 (JRuby 10):
[] jruby $ cx 3.4 ruby -v -e '"".instance_eval { }'
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
[] jruby $ ruby -v -e '"".instance_eval { }'
jruby 10.0.0.0-SNAPSHOT (3.4.0) 2025-02-18 493d0005c4 OpenJDK 64-Bit Server VM 21.0.5+11-LTS on 21.0.5+11-LTS +indy +jit [arm64-darwin]
-e:1: warning: literal string will be frozen in the future
In addition to causing warnings (and eventually errors) for tests that instance_eval against "frozen" strings, it greatly increases the overhead of non-def instance_eval:
[] jruby $ cx 3.4 ruby -v -rbenchmark -e '5.times { puts Benchmark.measure { 10_000_000.times { Object.new.instance_eval {} } } }'
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +PRISM [arm64-darwin24]
1.497619 0.009447 1.507066 ( 1.512211)
1.511998 0.011041 1.523039 ( 1.632321)
1.525667 0.017188 1.542855 ( 1.581538)
1.527588 0.011892 1.539480 ( 1.554302)
1.519021 0.012526 1.531547 ( 1.571109)
[] jruby $ ruby -v -rbenchmark -e '5.times { puts Benchmark.measure { 10_000_000.times { Object.new.instance_eval {} } } }'
jruby 10.0.0.0-SNAPSHOT (3.4.0) 2025-02-18 493d0005c4 OpenJDK 64-Bit Server VM 21.0.5+11-LTS on 21.0.5+11-LTS +indy +jit [arm64-darwin]
8.730000 0.470000 9.200000 ( 4.080593)
6.840000 0.470000 7.310000 ( 3.754422)
8.690000 0.440000 9.130000 ( 4.102935)
6.740000 1.020000 7.760000 ( 3.863980)
8.910000 0.740000 9.650000 ( 4.235179)
CRuby avoids creating the singleton eagerly by setting a singleton flag into the cref scope here:
https://github.com/ruby/ruby/blob/4ac75f6f6453bbf3c89f5b9ae02a03085b506ed5/vm_eval.c#L2233
This flag is later used when acquiring the class to be used for method definitions:
https://github.com/ruby/ruby/blob/4ac75f6f6453bbf3c89f5b9ae02a03085b506ed5/eval_intern.h#L192-L201
The performance improvement for def-less instance_eval alone would be worth making this change, but we eventually must eliminate the errors that happen for instance_eval against frozen objects (and in the shorter term, the warnings that come from instance_eval on a "chilled" string).
Currently, when calling
instance_evalagainst an object, JRuby will eagerly create a singleton class in preparation for any method definitions that might be coming. CRuby, on the other hand, defers creation until methods actually need to be defined, makinginstance_evalmuch cheaper in the absence of defs.We can see this effect by doing instance_eval against chilled strings, which triggers verbose warnings under 3.4 (JRuby 10):
In addition to causing warnings (and eventually errors) for tests that
instance_evalagainst "frozen" strings, it greatly increases the overhead of non-definstance_eval:CRuby avoids creating the singleton eagerly by setting a
singletonflag into thecrefscope here:https://github.com/ruby/ruby/blob/4ac75f6f6453bbf3c89f5b9ae02a03085b506ed5/vm_eval.c#L2233
This flag is later used when acquiring the class to be used for method definitions:
https://github.com/ruby/ruby/blob/4ac75f6f6453bbf3c89f5b9ae02a03085b506ed5/eval_intern.h#L192-L201
The performance improvement for def-less
instance_evalalone would be worth making this change, but we eventually must eliminate the errors that happen forinstance_evalagainst frozen objects (and in the shorter term, the warnings that come frominstance_evalon a "chilled" string).