在工作结束后,工作人员不会释放内存

问题描述 投票:0回答:1

场景:

我有一个在生产(heroku)中运行进程(sidekiq)的工作。该过程使用activerecord-import gem将数据(CSV)从S3导入数据库模型。此gem有助于批量插入数据。因此,dbRows变量在迭代CSV行时存储的所有ActiveRecord对象都设置了相当大的内存(都很好)。导入数据后(在:db_model.import dbRows中)dbRows被清除(应该是!)并处理下一个对象。

如:(简化脚本以便更好地理解)

def import
      ....
      s3_objects.contents.each do |obj|
          @cli.get_object({..., key: obj.key}, target: file) 
          dbRows = []
          csv = CSV.new(file, headers: false)
          while line = csv.shift
              # >> here dbRows grows and grows and never is freed!
              dbRows << db_model.new(
                field1: field1,
                field2: field2,
                fieldN: fieldN
              )
          end
          db_model.import dbRows
          dbRows = nil   # try 1 to freed array
          GC.start   # try 2 to freed memory
      end
      ....
end

问题:

进程运行时作业内存增加但是一旦作业完成,内存不会下降。它永远永远存在!

调试我发现dbRows看起来不会被垃圾收集,我学会了RETAINED对象以及内存如何在rails中运行。虽然我还没有找到一种方法来应用它来解决我的问题。

我希望一旦作业完成,dbRows上设置的所有引用都是GC并释放工作者内存。

任何帮助赞赏。

更新:我读到了关于weakref但我不知道是否有用。那里有什么见解?

ruby-on-rails ruby memory-management sidekiq activerecord-import
1个回答
0
投票

尝试批量导入CSV中的行,例如一次将行导入DB 1000行,这样您就不会保留以前的行,GC可以收集它们。无论如何,这对数据库都有好处(如果你从S3手中获取CSV IO对象,则从s3下载。

s3_io_object = s3_client.get_object(*s3_obj_params).body
csv = CSV.new(s3_io_object, headers: true, header_converters: :symbol)
csv.each_slice(1_000) do |row_batch|
  db_model.import ALLOWED_FIELDS, row_batch.map(&:to_h), validate: false
end

请注意,我没有实例化AR模型以节省内存,只传入哈希值并将activerecord-import告诉validate: false

此外,file参考来自哪里?这似乎是长寿的。

从您的示例中可以看出,但是对象的引用是否仍然可以通过环境中的库或扩展来全局保存?

有时这些东西很难被追踪,因为任何被调用的代码(包括外部库代码)都可以执行以下操作:

动态定义常量,因为它们永远不会得到GC

Any::Module::Or:Class.const_set('NewConstantName', :foo)

或者将数据添加到常量引用/拥有的任何内容中

SomeConstant::Referenceable::Globally.array << foo # array will only get bigger and contents will never be GC'd

否则,您可以做的最好的事情是使用一些内存分析工具,无论是在Ruby(内存分析gems)内部还是在Ruby外部(作业和系统日志),都可以尝试查找源代码。

© www.soinside.com 2019 - 2024. All rights reserved.