刚刚上了一个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的内存并没有明显变化
实在找不到北,于是借助网上一片文章
http://scottstuff.net/blog/articles/2006/08/17/memory-leak-profiling-with-rails
这篇文章介绍了一段代码来检测内存泄漏,遗憾的是对于如何通过这段代码搞定他自己的问题
一笔带过
代码如下:
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
源文档 < http://scottstuff.net/blog/articles/2006/08/17/memory-leak-profiling-with-rails>
为了能 更清楚地知道原因,我把这段代码直接加到测试机的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
)
结果中的每一行 表示每个对象在内存中的个数(后面的数字)和增减变化(前面的数字),每10秒计算一次,排出内存中的数量前20
如果你发现某个对象在内存中的个数一直高居不下,那说确实有问题
注意:如果我使用lighttpd,那么上面的结果是不会显示,也就是说连日志文件都不会产生,不知咋回事
用在测试机上我没有发现什么异常
不得已用在正式机上,确实看到了变化,我们自定义个某几个对象的内存数一直在增长,显然没有被垃圾回收的迹象,于是开始根据这几个对象查代码,查来查去也没查出个所以然。
正式机不行,但测试机是可以的,难道是因为环境的原因,采用最后一招,将正式机的环境改的和测试机一样, 结果问题就好,经过两天的观察,系统很稳定, 是不是已不存在泄漏还不敢说,但至少没有明显的泄漏,这个问题再进一步观察。
但令我疑惑的是难道ruby-1.8.5-p12 这个有问题?后来我把这个ruby-1.8.5-p12装到原来是正常的测试机也出现问题了。