ruby-on-rails - 如何处理 Ruby/Rails 中的内存泄漏
问题描述
我正在开发一个处理大量数据的 Rails 应用程序,由于内存泄漏(未释放的已分配对象),它使用了我计算机的所有内存,因此它停止了。
在我的应用程序中,数据以分层方式组织为一棵树,其中“X”级的每个节点都包含“X+1”级数据的总和。例如,如果级别“X+1”的数据包含城市的人口数量,则级别“X”的数据包含州的人口数量。这样,“X”级的数据就是“X+1”级(这里是人)的数据量相加得到的。
为了这个问题,考虑一个具有四个级别的树:国家、州、城市和社区,并且每个级别都映射到 Activerecords 表中(国家、州、城市、社区)。
数据是从填充树的叶子的 csv 文件中读取的,即邻域表。
此后,数据按以下顺序从底部(社区)流向顶部(国家):
1) Neighbourhoods data is summed to Cities;
2) after step 1 is completed, Cities data is summed to States;
3) after step 2 is completed, States data is summed to Country;
我使用的原理图代码如下:
1 cities = City.all
2 cities.each do |city|
3 city.data = 0
4 city.neighbourhoods.each do |neighbourhood|
5 city.data = city.data + neighbourhood.data
6 end
7 city.save
8 end
树的最低级别包含 380 万条记录。每次执行第 2-8 行时,都会汇总一个城市,执行第 8 行后,不再需要该子树,但永远不会释放它(内存泄漏)。加总 50% 的城市后,我所有的 8GB RAM 都消失了。
我的问题是我能做什么。由于我正在使用“小型”原型,因此无法购买更好的硬件。
我知道一种使它起作用的方法:为每个城市重新启动应用程序,但我希望有人有更好的主意。“最简单”的方法是强制垃圾收集器释放特定对象,但似乎不是一种方法(https://www.ruby-forum.com/t/how-do-i-force-ruby-释放内存/195515)。
从以下文章中,我了解到开发人员应该以一种“建议”垃圾收集器应该释放什么的方式来组织数据。也许另一种方法可以解决问题,但我看到的唯一替代方法是深度优先搜索方法,而不是我正在使用的反向广度优先搜索,但我不明白为什么它应该起作用。
到目前为止我读到的:
https://stackify.com/how-does-ruby-garbage-collection-work-a-simple-tutorial/
https://www.toptal.com/ruby/hunting-ruby-memory-issues
https://scoutapm.com/blog/ruby-garbage-collection
https://scoutapm.com/blog/manage-ruby-memory-usage
谢谢
解决方案
这并不是真正的内存泄漏情况。您只是不加说明地从表中加载数据,这将耗尽可用内存。
解决方案是批量加载数据库中的数据:
City.find_each do |city|
city.update(data: city.neighbourhoods.sum(&:data))
end
如果neighbourhoods.data
是一个简单的整数,则您不需要首先获取记录:
City.update_all(
'data = (SELECT SUM(neighbourhoods.data) FROM neighbourhoods WHERE neighbourhoods.city_id = cities.id)'
)
这将快一个数量级,并且由于所有工作都在数据库中完成,因此内存消耗很小。
如果您真的想将一堆记录加载到 Rails 中,请确保选择聚合而不是实例化所有这些嵌套记录:
City.left_joins(:neighbourhoods)
.group(:id)
.select(:id, 'SUM(neighbourhoods.data) AS n_data')
.find_each { |c| city.update(data: n_data) }
推荐阅读
- netty - netty 通道和线程和 eventLoop
- selenium-webdriver - 无法实例化类 TestRunner:将 Cucumber 与 TestNG 一起用于 Selenium 代码
- php - 将MongoDB中文档的子数组显示为php中的表
- android - 运行时权限在真实设备上不起作用
- c - 为什么我偶尔会得到不同的结果?【C语言】
- rest - 具有两条路径的 cookie 的 API 设计
- r - 如何在安装之前或之后修改 R 库源代码的特定脚本?
- google-apps-script - 谷歌脚本同一行中的多个条件和前一行中的值的验证
- java - 如何使用事务 java 引发错误
- r - “包含”功能