I detected a memory leak in my Rails code - that is, I found that the code was leaking, but not why. I shortened it to a test that does not require Rails:
require 'csspool' require 'ruby-mass' def report puts 'Memory ' + 'ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"'.strip.split.map(&:to_i)[1].to_s + 'KB' Mass.print end report
ruby-mass supposedly allows me to see all the objects in memory. CSSPool is a racc -based CSS parser . /home/jason/big.css is a 1.5 MB CSS file .
This outputs:
Memory 9264KB ================================================== Objects within [] namespace ================================================== String: 7261 RubyVM::InstructionSequence: 1151 Array: 562 Class: 313 Regexp: 181 Proc: 111 Encoding: 99 Gem::StubSpecification: 66 Gem::StubSpecification::StubLine: 60 Gem::Version: 60 Module: 31 Hash: 29 Gem::Requirement: 25 RubyVM::Env: 11 Gem::Specification: 8 Float: 7 Gem::Dependency: 7 Range: 4 Bignum: 3 IO: 3 Mutex: 3 Time: 3 Object: 2 ARGF.class: 1 Binding: 1 Complex: 1 Data: 1 Gem::PathSupport: 1 IOError: 1 MatchData: 1 Monitor: 1 NoMemoryError: 1 Process::Status: 1 Random: 1 RubyVM: 1 SystemStackError: 1 Thread: 1 ThreadGroup: 1 fatal: 1 ================================================== Memory 258860KB ================================================== Objects within [] namespace ================================================== String: 7456 RubyVM::InstructionSequence: 1151 Array: 564 Class: 313 Regexp: 181 Proc: 113 Encoding: 99 Gem::StubSpecification: 66 Gem::StubSpecification::StubLine: 60 Gem::Version: 60 Module: 31 Hash: 30 Gem::Requirement: 25 RubyVM::Env: 13 Gem::Specification: 8 Float: 7 Gem::Dependency: 7 Range: 4 Bignum: 3 IO: 3 Mutex: 3 Time: 3 Object: 2 ARGF.class: 1 Binding: 1 Complex: 1 Data: 1 Gem::PathSupport: 1 IOError: 1 MatchData: 1 Monitor: 1 NoMemoryError: 1 Process::Status: 1 Random: 1 RubyVM: 1 SystemStackError: 1 Thread: 1 ThreadGroup: 1 fatal: 1 ==================================================
You can see the memory going way. Some counters go up, but there are no CSSPool-specific objects. I used the ruby-mass index method to check for objects that are referenced, for example:
Mass.index.each do |k,v| v.each do |id| refs = Mass.references(Mass[id]) puts refs if !refs.empty? end end
But then again, it gives me nothing to do with CSSPool, just gem info and the like.
I also tried to output "GC.stat" ...
puts GC.stat CSSPool::CSS::Document.parse(File.new('/home/jason/big.css')) ObjectSpace.garbage_collect sleep 1 puts GC.stat
Result:
{:count=>4, :heap_used=>126, :heap_length=>138, :heap_increment=>12, :heap_live_num=>50924, :heap_free_num=>24595, :heap_final_num=>0, :total_allocated_object=>86030, :total_freed_object=>35106} {:count=>16, :heap_used=>6039, :heap_length=>12933, :heap_increment=>3841, :heap_live_num=>13369, :heap_free_num=>2443302, :heap_final_num=>0, :total_allocated_object=>3771675, :total_freed_object=>3758306}
As far as I understand, if an object is not referenced and garbage collection occurs, then this object should be cleared from memory. But it seems that this is not what is happening here.
I also read about level C memory leaks, and since CSSPool uses Racc, which uses C code, I think this is possible. I ran my code through Valgrind:
valgrind --partial-loads-ok=yes --undef-value-errors=no --leak-check=full --fullpath-after= ruby leak.rb 2> valgrind.txt
The results are here . I'm not sure if this confirms a C-level leak, since I also read that Ruby works with memory that Valgrind does not understand.
Used Versions:
- Ruby 2.0.0-p247 (this is what launches my Rails application)
- Ruby 1.9.3-p392-ref (for testing with ruby ββmass)
- ruby mass 0.1.3
- CSSPool 4.0.0 from here
- CentOS 6.4 and Ubuntu 13.10