刚刚上了一个rails(ruby-1.8.5-p12 +rails1.2.3+lighttpd)项目,却出现了严重的内存泄漏
上线不到几个小时,内存就好到1G, 一般情况下每个fcgi进程也就20-30+m。
实在不知道怎么弄,网上看看说fcig可能会有内存泄漏,于是换成mongrel1.01,结果还是一样
没办法了,只有通过测试机(rails-1.8.6-p10+rails1.2.5+mongrel1.0.1)来测试,通过httperf高负载请求
几个访问量比较大的url, 发现mongrel1.0.1的内存并没有明显变化
实在找不到北,于是借助网上一片文章
这篇文章介绍了一段代码来检测内存泄漏,遗憾的是对于如何通过这段代码搞定他自己的问题
一笔带过
代码如下:
class MemoryProfiler
DEFAULTS={:delay=>10,:string_debug=>false}
def self.start(opt={})
opt=DEFAULTS.dup.merge(opt)
Thread.newdo
prev=Hash.new(0)
curr=Hash.new(0)
curr_strings=[]
delta=Hash.new(0)
file=File.open('log/memory_profiler.log','w')
loopdo
begin
GC.start
curr.clear
curr_strings=[]ifopt[:string_debug]
ObjectSpace.each_objectdo|o|
curr[o.class]+=1#Marshal.dump(o).size rescue 1
ifopt[:string_debug]ando.class ==String
curr_strings.pusho
end
end
ifopt[:string_debug]
File.open("log/memory_profiler_strings.log.#{Time.now.to_i}",'w')do|f|
curr_strings.sort.eachdo|s|
f.putss
end
end
curr_strings.clear
end
delta.clear
(curr.keys+delta.keys).uniq.eachdo|k,v|
delta[k]=curr[k]-prev[k]
end
file.puts"Top 20"
delta.sort_by{|k,v|-v.abs}[0..19].sort_by{|k,v|-v}.eachdo|k,v|
file.printf"%+5d: %s (%d) ",v,k.name,curr[k]unlessv==0
end
file.flush
delta.clear
prev.clear
prev.updatecurr
GC.start
rescueException=>err
STDERR.puts"** memory_profiler error: #{err}"
end
sleepopt[:delay]
end
end
end
end
为了能 更清楚地知道原因,我把这段代码直接加到测试机的environment.rb中,并在后面在补上
MemoryProfiler.start
这并非良好的代码组织方式,但是在时间仓促的情况下确实达到了效果,我通过
在项目的根目录下执行tail -f log/memory_profiler.log
看到了结果内似如下:
Top 20
+252: String (33975)
+53: Array (1751)
+23: Hash (376)
+8: Time (67)
+6: MatchData (10)
+5: HashWithIndifferentAccess (10)
+3: StringIO (9)
+2: Bignum (21)
+2: CGI::Session (4)
+2: ActionController::CgiRequest (4)
+2: ActionController::Flash::FlashHash (4)
+2: ActionController::CgiResponse (4)
+2: Digest::MD5 (2)
+2: Regexp (1172)
+1: Mongrel::HttpResponse (3)
Top 20
+2: Proc (454)
+1: IOError (6)
+1: Mysql::Error (6)
+1: Mysql::Net (7)
-1: Mongrel::HttpParams (2)
-1: Mongrel::HeaderOut (2)
-1: Mongrel::HttpRequest (2)
-1: ActionController::CookieJar (2)
-1: CGI::Cookie (2)
-1: Thread (5)
-1: Float (61)
-1: (1)
+252: String (33975)
+53: Array (1751)
+23: Hash (376)
+8: Time (67)
+6: MatchData (10)
+5: HashWithIndifferentAccess (10)
+3: StringIO (9)
+2: Bignum (21)
+2: CGI::Session (4)
+2: ActionController::CgiRequest (4)
+2: ActionController::Flash::FlashHash (4)
+2: ActionController::CgiResponse (4)
+2: Digest::MD5 (2)
+2: Regexp (1172)
+1: Mongrel::HttpResponse (3)
Top 20
+2: Proc (454)
+1: IOError (6)
+1: Mysql::Error (6)
+1: Mysql::Net (7)
-1: Mongrel::HttpParams (2)
-1: Mongrel::HeaderOut (2)
-1: Mongrel::HttpRequest (2)
-1: ActionController::CookieJar (2)
-1: CGI::Cookie (2)
-1: Thread (5)
-1: Float (61)
-1: (1)
结果中的每一行 表示每个对象在内存中的个数(后面的数字)和增减变化(前面的数字),每10秒计算一次,排出内存中的数量前20
如果你发现某个对象在内存中的个数一直高居不下,那说确实有问题
注意:如果我使用lighttpd,那么上面的结果是不会显示,也就是说连日志文件都不会产生,不知咋回事
用在测试机上我没有发现什么异常
不得已用在正式机上,确实看到了变化,我们自定义个某几个对象的内存数一直在增长,显然没有被垃圾回收的迹象,于是开始根据这几个对象查代码,查来查去也没查出个所以然。
正式机不行,但测试机是可以的,难道是因为环境的原因,采用最后一招,将正式机的环境改的和测试机一样, 结果问题就好,经过两天的观察,系统很稳定, 是不是已不存在泄漏还不敢说,但至少没有明显的泄漏,这个问题再进一步观察。
但令我疑惑的是难道ruby-1.8.5-p12 这个有问题?后来我把这个ruby-1.8.5-p12装到原来是正常的测试机也出现问题了。