当前位置:  开发笔记 > 后端 > 正文

Ruby内存管理

如何解决《Ruby内存管理》经验,为你挑选了4个好方法。

我已经使用Ruby一段时间了,我发现,对于更大的项目,它可能会占用相当多的内存.有哪些减少Ruby内存使用的最佳实践?

请让每个答案都有一个"最佳实践",并让社区投票.

Alex Kovshov.. 27

使用大量ActiveRecord对象时要非常小心......当在循环中处理这些对象时,如果在每次迭代中使用ActiveRecord的has_many,belongs_to等加载它们的相关对象 - 内存使用量会增长很多,因为每个对象都是属于一个数组增长...

以下技术帮助了我们很多(简化示例):

students.each do |student|
  cloned_student = student.clone
  ...
  cloned_student.books.detect {...}
  ca_teachers = cloned_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
  ca_teachers.blah_blah
  ...
  # Not sure if the following is necessary, but we have it just in case...
  cloned_student = nil
end

在上面的代码中,"cloned_student"是增长的对象,但由于它在每次迭代结束时"无效",因此对于大量学生来说这不是问题.如果我们没有做"克隆",循环变量"student"会增长,但由于它属于一个数组 - 只要数组对象存在,它就不会释放它所使用的内存.

不同的方法也有效:

students.each do |student|
  loop_student = Student.find(student.id) # just re-find the record into local variable.
  ...
  loop_student.books.detect {...}
  ca_teachers = loop_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
  ca_teachers.blah_blah
  ...
end

在我们的生产环境中,我们有一个后台进程无法完成一次,因为8Gb的RAM还不够.在这个小小的改变后,它使用不到1Gb来处理相同数量的数据......



1> Alex Kovshov..:

使用大量ActiveRecord对象时要非常小心......当在循环中处理这些对象时,如果在每次迭代中使用ActiveRecord的has_many,belongs_to等加载它们的相关对象 - 内存使用量会增长很多,因为每个对象都是属于一个数组增长...

以下技术帮助了我们很多(简化示例):

students.each do |student|
  cloned_student = student.clone
  ...
  cloned_student.books.detect {...}
  ca_teachers = cloned_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
  ca_teachers.blah_blah
  ...
  # Not sure if the following is necessary, but we have it just in case...
  cloned_student = nil
end

在上面的代码中,"cloned_student"是增长的对象,但由于它在每次迭代结束时"无效",因此对于大量学生来说这不是问题.如果我们没有做"克隆",循环变量"student"会增长,但由于它属于一个数组 - 只要数组对象存在,它就不会释放它所使用的内存.

不同的方法也有效:

students.each do |student|
  loop_student = Student.find(student.id) # just re-find the record into local variable.
  ...
  loop_student.books.detect {...}
  ca_teachers = loop_student.teachers.detect {|teacher| teacher.address.state == 'CA'}
  ca_teachers.blah_blah
  ...
end

在我们的生产环境中,我们有一个后台进程无法完成一次,因为8Gb的RAM还不够.在这个小小的改变后,它使用不到1Gb来处理相同数量的数据......



2> Orion Edward..:

不要滥用符号.

每次创建符号时,ruby都会在其符号表中放入一个条目.符号表是一个永远不会被清空的全局哈希.
这在技术上不是内存泄漏,但它表现得像一个.符号不占用太多内存,所以你不需要过于偏执,但要注意这一点是值得的.

一般准则:如果你实际上在代码中输入了符号,那就很好(毕竟你只有一定数量的代码),但是不要在动态生成或用户输入字符串上调用to_sym,因为这会打开大门可能不断增加的数字


在Ruby 2.2中,符号将被垃圾收集.

3> Orion Edward..:

不要这样做:

def method(x)
  x.split( doesn't matter what the args are )
end

或这个:

def method(x)
  x.gsub( doesn't matter what the args are )
end

两者都将在ruby 1.8.5和1.8.6中永久泄漏内存.(不确定1.8.7,因为我没有尝试过,但我真的希望它已修复.)解决方法是愚蠢的,涉及创建一个局部变量.你不必使用本地,只需创建一个...

这样的事情就是为什么我对红宝石语言有很多的爱,但不尊重MRI


看起来这在Ruby 1.8.7中不再发生了.

4> Orion Edward..:

注意C扩展本身分配大块内存.

例如,当您使用RMagick加载图像时,整个位图会加载到ruby进程内的内存中.根据图像的大小,这可能是30微米左右.
但是,大部分内存都是由RMagick自己分配的.所有ruby都知道是一个包装器对象,它很小(1).
Ruby只认为它占用了很少的内存,所以它不会打扰运行GC.实际上它持有30兆.
如果你循环说出10个图像,你可以非常快地运行自己的内存.

首选的解决方案是手动告诉C库清理内存本身 - RMagick有一个破坏!这样做的方法.但是,如果您的库没有,您可能需要自己强行运行GC,即使通常不鼓励这样做.

(1):Ruby C扩展具有回调,当ruby运行时决定释放它们时它们将被运行,因此内存最终会在某些时候成功释放,可能不会很快.


在这种情况下,无需对blob的引用并调用GC.start _can_释放内存.RMagic 2.2应该在内部处理(但仍然不如nil + GC有效)
推荐阅读
郑小蒜9299_941611_G
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有