Я обнаружил утечку памяти в моем коде Rails - то есть я обнаружил, что код утечек, но не почему. Я сократил его до теста, который не требует 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
# note I do not store the return value here
CSSPool::CSS::Document.parse(File.new('/home/jason/big.css'))
ObjectSpace.garbage_collect
sleep 1
report
ruby-mass предположительно позволяет мне видеть все объекты в памяти. CSSPool - это синтаксический анализатор CSS, основанный на racc. /home/jason/big.css - это файл CSS размером 1,5 МБ.
Это выводит:
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
==================================================
Вы можете увидеть память собирается путь. Некоторые счетчики идут вверх, но никаких объектов, специфичных для CSSPool, нет. Я использовал метод индекса "ruby-mass" для проверки объектов, на которые есть ссылки, например:
Mass.index.each do |k,v|
v.each do |id|
refs = Mass.references(Mass[id])
puts refs if !refs.empty?
end
end
Но опять же, это не дает мне ничего связанного с CSSPool, просто информация о гемах и тому подобное.
Я также попытался вывести "GC.stat"...
puts GC.stat
CSSPool::CSS::Document.parse(File.new('/home/jason/big.css'))
ObjectSpace.garbage_collect
sleep 1
puts GC.stat
Результат:
{: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}
Насколько я понимаю, если на объект не ссылаются и происходит сборка мусора, то этот объект следует очистить из памяти. Но, похоже, это не то, что здесь происходит.
Я также читал об утечках памяти уровня C, и, поскольку CSSPool использует Racc, который использует код C, я думаю, что это возможно. Я запустил свой код через Valgrind:
valgrind --partial-loads-ok=yes --undef-value-errors=no --leak-check=full --fullpath-after= ruby leak.rb 2> valgrind.txt
Результаты здесь. Я не уверен, подтверждает ли это утечку уровня C, так как я также читал, что Ruby работает с памятью, которую Valgrind не понимает.
Используемые версии:
- Ruby 2.0.0-p247 (это то, что запускает мое приложение Rails)
- Ruby 1.9.3-p392-ref (для тестирования с рубиновой массой)
- рубиновая масса 0.1.3
- CSSPool 4.0.0 отсюда
- CentOS 6.4 и Ubuntu 13.10