【发布时间】:2013-11-19 09:11:03
【问题描述】:
我的应用在 Puma (2.4) 集群模式下运行,有 4 个工作人员。 最初,它们总共使用不到 2GB 的 RAM,但会不断增长,最终在运行 20 小时后占用 7GB。
通过使用 ObjectSpace,我发现字符串对象的数量增加得非常快,每个 worker 中的对象从大约 300k 增加到 4-5 百万个。
然后我使用以下脚本将这些字符串按前 60 个字符分组并执行计数:
counts = Hash.new(0)
ObjectSpace.each_object do |o|
next unless (o.class == String)
counts[o[0,60]] += 1
end
counts = counts.to_a.sort_by(&:last);
puts counts[-10..-1]
事实证明,这些字符串中的大多数都是来自 Active Record 的 SQL 日志记录
ObjectSpace.count_objects
# result
{
:TOTAL => 2439593,
:FREE => 209200,
:T_OBJECT => 65944,
:T_CLASS => 11343,
:T_MODULE => 2003,
:T_FLOAT => 13,
:T_STRING => 1821445,
:T_REGEXP => 6570,
:T_ARRAY => 157012,
:T_HASH => 27477,
:T_STRUCT => 1406,
:T_BIGNUM => 1393,
:T_FILE => 142,
:T_DATA => 75081,
:T_MATCH => 1334,
:T_COMPLEX => 1,
:T_RATIONAL => 2809,
:T_NODE => 51890,
:T_ICLASS => 4530
}
# top 10 string
["PricingRule Exists: SELECT" , 74632]
[": SELECT COUNT(*) FROM `re" , 85454]
["CACHE: SELECT `companies`" , 93045]
["PricingRule Load: SELECT " , 114169]
["Page Load: SELECT `pages`" , 140245]
[": SELECT COUNT(*) FROM `pa" , 182274]
["Customer Load: SELECT `cu" , 191972]
["Company Load: SELECT `com" , 253025]
["Page Load: SELECT `pages`." , 320267]
["DestinationCountry Load: S" , 413299]
我使用 Rails 4、Ruby 2、mysql2(v0.3.13) 并将日志级别设置为警告,但这些 SQL 字符串仍被存储并在内存中不断增加。
有人对这个问题有任何想法或经验吗?如果您能提供帮助,我将不胜感激。
谢谢!
【问题讨论】:
-
几年前我在 EventMachine gem 上看到过类似的问题。你的 C 编程有多好?我们可能需要弄脏手。看看这个:blog.nelhage.com/2013/03/tracking-an-eventmachine-leak
-
如果您按照那篇文章进行操作,您会意识到每个对象 1k 的 200 万个字符串对象只有 2G。你提到7G的内存使用量。要么你的字符串真的很长(在这种情况下你很幸运,因为你已经发现了问题)或者问题更深,你可能需要深入研究 C 代码。
-
@Chandranshu 感谢您的回复,每个工作人员有 200 万个字符串对象,有 4 个工作人员,它们最多可以占用 8GB,这似乎是泄漏的原因。
-
哦,好的。然后你几乎把它钉牢了。您只需要查看哪些对象引用了这些字符串。由于所有者对象,字符串没有被垃圾收集。如果您尝试在臃肿的应用程序上执行此操作,将会很困难。只需尝试分析 10 分钟的 Objectspace。
-
您可能已经知道这一点,但在开始分析“对象空间的 10 分钟”之前,请确保您手动运行一次垃圾收集器。
标签: mysql ruby-on-rails activerecord memory-leaks