【问题标题】:ActiveRecord not releasing memoryActiveRecord 不释放内存
【发布时间】:2020-09-17 18:59:30
【问题描述】:

我有一个导出工作,从我们的 MySQL 数据库中导出大量数据。随着数据的增长,我注意到 sidekiq 的工作占用了太多的内存。服务器有 32GB,导出完成后需要 28GB。当我停止 sidekiq 进程时,内存使用量降至 8GB。

我已经按照这里的指南https://github.com/mperham/sidekiq/wiki/Problems-and-Troubleshooting

  • 使用MALLOC_ARENA_MAX=2防止内存碎片
  • 清除查询缓存ActiveRecord::Base.connection.clear_query_cache

我正在使用 Ruby 2.6.5p114,并尝试通过在生产中创建新的 rails 应用程序并将我的数据库用作后端来隔离问题:

gem install rails --version 5.2.4.3
rails new debug -d mysql

我创建了一个空模型以避免代码中可能导致问题的自定义方法:

class Variant < ApplicationRecord
end

此脚本只是从数据库中加载 1 个 Mio 对象并打印内存使用情况:

# memory.rb

def memory
  (`ps -o rss= -p #{Process.pid}`.to_i.to_f / 1024).to_s + " MB"
end

def load_variants
  puts "load_variants..."
  Variant.uncached do
    variants = Variant.limit(1_000_000).to_a
    puts "variant.count: #{variants.count}"
  end
end

puts memory
load_variants
puts memory

puts "GC.start..."
GC.start
puts memory

# second run
load_variants
puts memory

puts "GC.start..."
GC.start
puts memory

这是输出:

root@6e79d7a97d9c:/usr/src/debug# rails r memory.rb
76.93359375 MB
load_variants...
variant.count: 1000000
2436.3125 MB
GC.start...
2421.046875 MB
load_variants...
variant.count: 1000000
2436.3828125 MB
GC.start...
2436.3984375 MB
  1. 76.93359375 MB开头
  2. 加载1个Mio对象后,内存增加到2436.3125 MB
  3. 垃圾回收将内存减少到2421.046875 MB但我预计会有明显更高的下降!
  4. 有趣的是,第二次运行,只会增加内存到2436.3828125 MB
  5. 最后一个GC.start不知何故增加内存一点点到2436.3984375 MB

所以我想知道这是怎么回事? ActiveRecord 中一定有一些我不知道的东西,我想了解这一切是如何工作的,以及为什么没有释放内存。

按照这个逻辑,每个读取数据的请求都会增加内存,但我认为在请求-响应周期内使用时会有所不同。

【问题讨论】:

标签: ruby-on-rails ruby memory memory-leaks rails-activerecord


【解决方案1】:

在 Ruby 中加载分布在内存中的大对象(而不是像 String 这样的对象在连续内存中)往往会产生这种效果,因为标记和扫描算法无法将整个内存块返回到操作系统。如果您开始解析大型 JSON 文件(如 10+MB),您将获得类似的效果,因为生成的 Hash(由大量其他对象组成)将与仍然具有活动引用的其他对象一起放置在多个内存块中因此 Ruby 无法释放该块。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-29
    • 2011-06-30
    • 2016-09-14
    • 2015-08-19
    • 2012-05-15
    • 2018-01-26
    • 2011-09-30
    • 2012-03-18
    相关资源
    最近更新 更多