【发布时间】:2011-07-19 04:24:45
【问题描述】:
你好,
我们最近将一个应用程序更新到 Rails 3.0.4(在线开发服务器上的 3.0.5)。从 2.3.10 到 3.0.4 的大部分更改是由于过时或过时的插件和 gem 造成的,并且可以相对容易地解决。但有一件事让我很生气:
在开发模式下,每个 Web 请求都会导致服务器进程分配比以前大约 50-60 MB 更多的内存。此内存在请求后没有被释放,至少不是全部。在 10-20 次请求之后,每个 Ruby 实例消耗了超过 500 MB 的 RAM,而我们之前的 Rails 2.3.10 实例很少超过 200 MB。
这使得我们无法运行我们的 1300 次测试,因为在测试结束之前开发机器的 4GB RAM 已被填满。它只发生在cache_classes = false 的开发模式下。如果我将 cache_classes 切换为 true,Rails 实例将消耗大约 200MB 的内存,然后停留在那里。但是,在测试期间,即使 cache_classes = true,内存使用量也会增加。
我查询了 ObjectSpace,发现每次请求都会创建大约 3500 个新 Proc、多达 50'000 个新字符串以及 3000 个新哈希和数组,但未释放。这些字符串(转储时)包含我的整个源代码,包括插件和 gem、文档、源代码 cmets 和路径名。 (为什么?)
为了找到造成这种情况的原因,我尝试了以下方法:(每次更改后,我都使用 ab -n 50 锤击应用程序。)
- 我创建了一个全新的 Rails 3 应用程序,其中包含单个资源和控制器以及 SQLite3 数据库。 内存使用量从 60 MB 开始,一直低于 80 MB。
- 我将 'sqlite3' 更改为 'pg' 并将新的 Rails 3 应用程序指向我现有的 Postgres DB。 内存使用量从 110 MB 开始,并没有超过 130 MB。 (附带问题:为什么 Postgres gem 比 SQLite3 gem 使用更多的内存?)
- 我将我的 Gemfile 和 Gemfile.lock 从损坏的 Rails3 应用程序复制到基本应用程序并运行 bundle install。 没有变化,无论发出多少请求,内存都保持在 115MB 左右。
- 我在损坏的 Rails3 应用程序中创建了一个空的“def FooController; def foo; render :text => 'foo' end; end”。 内存使用量增长较慢,但在请求后仍不会停止增长。
- 我删除了除 FooController 路由之外的所有路由。 没有变化。
- 我禁用了所有宝石,但以下除外:
pg, rails, aasm, will_paginate, geokit-rails3, koala, omniauth, paperclip。 没有变化。 - 我禁用了 ApplicationController 中的每个 before_filter 和 after_filter 以及 environment.rb 中的每个非必要的
include。我还将 boot.rb、environment.rb 和 application.rb 与我的基本 Rails 3 应用程序同步,除了五个相对简单的观察器,自动加载 /lib 和 filter_parameters 中的文件。 没有变化。每个新请求仍会额外消耗 10-50 MB 的 RAM。
如果您知道这里出了什么问题,以及内存泄漏可能在哪里,我将非常感谢您的帮助。我在 OS X Snow Leopard 上运行 Rails 3.0.4,在 Debian Lenny 上运行 Rails 3.0.5,以及
谢谢!
越来越近了:
我已经删除了每个插件、每个 gem、每个扩展以及不是我自己编写的所有内容,因此我的应用程序基本上是赤裸裸的。特别是,我删除了这些插件:acts_as_list, acts_as_tree, asset_packager, forgot_password, fudge_form, fudge_scaffold, paperclippolymorph, query_trace, rails_upgrade, repeated_auto_complete-0.1.0, role_requirement, to_select, validates_url, and ym4r_gm。
现在我的应用程序 - 只有上面的 FooController 仍然有效! - 以 65MB 启动并且永远不会超过 75MB 的 RAM,即使在使用 ab -n 1000 -c1 敲击它之后(使用 ApacheBench 向 /foo 发出 1000 个 HTTP 请求)。不幸的是,如果没有插件,这也是唯一有效的 URI。
经过一番挖掘,似乎是 Restful Authentication 和 Acts As State Machine (AASM) 插件之间的组合导致了内存泄漏。另见https://github.com/Satish/restful-authentication/issues#issue/11。我还不确定为什么,只是在我的准系统项目中执行“包含 AASM”并不会单独导致 RAM 使用量增长。
我会进一步调查。
找到罪魁祸首
这是 AASM。在 Rails 3 中,它似乎泄漏了 AASM::xxx 对象实例。见
- https://github.com/jeffp/enumerated_attribute/issues/#issue/20
- https://github.com/rubyist/aasm/issues/31
- https://github.com/Satish/restful-authentication/issues/#issue/11
找到第二个罪魁祸首
rspec 中还有另一个内存泄漏。这使得我的测试几乎无法忍受,即使在删除 AASM 之后也是如此,因为两个并行运行的 rspec 任务(使用 https://github.com/grosser/parallel_tests)最后占用了将近 3GB 的内存。见https://github.com/rspec/rspec-core/issues/#issue/321。
【问题讨论】:
-
您的服务器使用什么?乘客或 Rails 内置服务器?
-
我试过Passenger 3.0.3和WEBrick,都表现出相同的行为。
-
我遇到了同样的问题,所以我删除了 AASM,但泄漏并没有消失。您如何确定导致泄漏的宝石?
-
@asoules,一般来说,“分而治之”。如果您怀疑某个插件,请删除一半插件并进行测试。如果错误仍然存在,则您知道错误在哪一半。继续直到找到罪魁祸首。
-
如果你为你的问题写了一个答案,那就太好了。
标签: ruby-on-rails-3 postgresql memory-leaks rspec-rails