The following script takes about 2 seconds (tested on 2.6.6 and 2.7.2) to get from 'rescue' to 'ensure' on my machine, when executed as ruby --disable-did_you_mean script.rb. The time is taken by getting the e.message.
class Environment
def run
@data = (1..5_000_000).to_h {|x| [x, x]}
trigger_name_error
rescue Exception => e
start = Time.now
e.message
ensure
puts "In ensure after #{Time.now - start}"
end
end
Environment.new.run
The time is much lower if you:
change the instance variable to a local variable
change #run to a class method
trigger something other than a NameError
The time is roughly linear in the size of @data.
The time roughly doubles if you enable did_you_mean.
Since I'm regularly operating in such contexts with large instance variables, I noted the slowness of NameErrors and went looking for the cause, whittling it down to the above script. Not sure if this should be considered a bug, but I guess it is probably undesirable?
Note the :count and :total_allocated_objects attributes. To generate the error message we allocated around 20 million objects and triggered the GC more than 4000 times.
Now, if we make data a local variable, we see the following output:
Note that the GC only ran once and allocated less than 1000 objects. The difference is that the outputted error message does not have the @data instance variable in the output (error message is about 87 million characters with @data vs. 93 characters without).