最近一个 rails app 内存老是报警,观察发现 ruby 进程消耗的内存会成倍的增长,从原来的 200 多兆,增长到四五百兆,然后是七八百兆,最多的时候是 1G 以上,用的是 unicorn,6 个 ruby 进程,请求多的话,会有四五个进程同时消耗内存都快 1G 了,搞得服务器经常报警,负载很高,而且 ruby 进程的内存只会增加,不会减少。
怎么样才能快速的找出,是哪里出了问题,那里的代码消耗内存最多?
下面是解决方法:
top -p $(pgrep -d',' ruby) #实时观察每个ruby进程的内存开销
#application.rb
around_filter :log_rss #具有before_filter和after_filter的功能
def log_rss
before_rss,before_rss_t = _worker_rss #action前该进程的内存
yield #执行action
after_rss,after_rss_t = _worker_rss #action后该进程的内存
after_rss_t ||= 0
before_rss_t ||= 0
if after_rss_t - before_rss_t > 100000000 #大于100M(大概)
logger.info "#{controller_name}_#{action_name} rss info #{Process.pid} VmRSS: #{before_rss}----#{after_rss}"
end
end
def _worker_rss
proc_status = "/proc/#{Process.pid}/status"
if File.exists? proc_status
open(proc_status).each_line { |l|
if l.include? 'VmRSS'
ls = l.split
if ls.length == 3
value = ls[1].to_i
unit = ls[2]
val = case unit.downcase
when 'kb'
value*(1024**1)
when 'mb'
value*(1024**2)
when 'gb'
value*(1024**3)
end
return ["#{value} #{unit}",val]
end
end
}
["0",0]
end
["0",0]
end
这样就可以找到哪些 action 是有问题的了,接下来就要查找 action 中是那部分代码写的有问题了,原理还是同 log_rss 一样,先记录可以代码执行前的 rss,在记录可以代码执行后的 rss,两者对比,找到最费内存的代码!