Ever-increasing memory with a custom runner and a single spec file
Created by: agis
Subject of the issue
In a custom RSpec runner I'm using, I've noticed an ever-increasing memory usage with each spec file (the same file, actually) that the runner executes. I don't know if this expected or not, so I'm opening an issue here to get your insights as well.
Your environment
- Ruby version: 2.5.5 (also tested with 2.7.1)
- rspec-core version: 3.9.2
Steps to reproduce
Clone the reproduction repository:
$ git clone git@github.com:agis/rspec-memory-issue.git
$ cd rspec-memory-issue
$ bundle install
$ bundle exec ruby test.rb
Expected behavior
I'd expect memory usage to reach a plateau.
Actual behavior
Memory usage grows indefinitely (reaches some gigabytes after 2-3 minutes). This is probably due to the fact that RSpec creates a class for each describe/it block if the spec file. However, these classes never get garbage collected (which is expected I suppose), but they also get redefined every time the same file gets loaded.
For instance, ObjectSpace.each_object(RSpec::Core::ExampleGroup).each.count
shows ever increasing values with each loop:
iteration #1: 28 (a few kilobytes)
...
iteration #1096: 3232 (100MB)
...
iteration #4100: 8552 (325MB)
.....
Likewise, the defined classes look like this:
ObjectSpace.each_object(Class) { |x| puts x }
#...
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa::FkofkofkoekoFkeoAkofeKaokfEo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::HdisajfidajidjsIajfdklsajFkldsajLkfdsa
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm::HeyHeyHey
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo::Hmmmmmmm
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
RSpec::ExampleGroups::SomethingWsomewhereFoFoFo
=> 45350
So they're repeatedly defined as different class objects. Is this expected behavior?
Thanks in advance.