创建许多新对象时内存膨胀

时间:2012-05-05 14:00:08

标签: ruby-on-rails ruby memory-management

当我运行它然后在OSX活动监视器中观察我的ruby进程的内存消耗时,内存增加大约3 MB / s。

如果我删除了事务,它会将内存消耗减少一半,但内存占用量仍在增加。我的生产应用程序中存在一个问题,Heroku因其内存消耗而导致该进程死亡。

有没有办法在不增加内存的情况下执行以下操作?如果我注释掉.save行,那么它没关系,但当然这不是解决方案。

ActiveRecord::Base.transaction do
  10000000.times do |time|
    puts "---- #{time} ----"
    a = Activity.new(:name => "#{time} Activity")
    a.save!(:validate => false)
    a = nil
  end
end

我使用delayed_job运行此功能。

2 个答案:

答案 0 :(得分:5)

a = nil行是不必要的,您可以将其删除。

每次循环时你都会创建很多对象 - 两个字符串,两个哈希值和一个Activity对象,所以我不会感到惊讶你的内存使用率很高,特别是当你循环1000万次时!似乎没有更高效的内存来编写此代码。

我能想到的减少内存使用量的唯一方法是每 x 次迭代手动启动垃圾收集器。机会是Ruby的GC不够积极。但是,您不希望每次迭代都调用它,因为这会极大地降低代码速度。也许你可以使用每100次迭代作为起点并从那里开始。你必须分析和测试什么是最有效的。

GC is here的文档。

答案 1 :(得分:1)

我知道这是一个古老的问题,但我必须提出另一种激进的方法:

ActiveRecord::Base.transaction do
  10000000.times do |time|
    puts "---- #{time} ----"
    sql = <<SQL
INSERT INTO activities ("name") VALUES ("#{time}")
SQL
    Activity.connection.execute(sql)
  end
end

关键是如果插入很简单,并且您已经跳过任何ActiveModel验证,那么就没有理由首先实例化一个activerecord对象。通常它不会受到伤害,但是因为在这种情况下它会伤害你,我认为你会用这种方式少用内存。