Ruby内存管理
我使用Ruby已经有一段时间了,我发现,对于更大的项目,它可能会占用相当多的内存。在Ruby中减少内存使用的最佳实践是什么Ruby内存管理,ruby,memory,memory-management,coding-style,Ruby,Memory,Memory Management,Coding Style,我使用Ruby已经有一段时间了,我发现,对于更大的项目,它可能会占用相当多的内存。在Ruby中减少内存使用的最佳实践是什么 请让每个答案都有一个“最佳实践”,并让社区投票表决 小心那些自己分配大块内存的C扩展 例如,当您使用RMagick加载图像时,整个位图将加载到ruby进程内的内存中。这可能是30兆左右,具体取决于图像的大小。 然而,大部分内存是由RMagick自己分配的。ruby只知道一个包装器对象,它很小(1)。 Ruby只认为它只保留了很少的内存,所以它不会费心运行GC。事实上,它的
- 请让每个答案都有一个“最佳实践”,并让社区投票表决
然而,大部分内存是由RMagick自己分配的。ruby只知道一个包装器对象,它很小(1)。
Ruby只认为它只保留了很少的内存,所以它不会费心运行GC。事实上,它的容量为30兆欧。
如果你循环10张图片,你会很快耗尽自己的记忆 首选的解决方案是手动告诉C库清理内存本身-RMagick有一个destroy!方法来实现这一点。但是,如果您的库没有这样做,您可能需要自己强制运行GC,尽管这通常是不鼓励的 (1) :Ruby C扩展有回调,当Ruby运行时决定释放它们时,回调将运行,因此内存最终会在某个时候成功释放,只是可能还不够快。不要滥用符号 每次创建符号时,ruby都会在其符号表中放入一个条目。符号表是一个永远不会被清空的全局哈希。
从技术上讲,这不是内存泄漏,但其行为与内存泄漏类似。符号不会占用太多的内存,所以你不必太偏执,但意识到这一点是值得的 一般原则:如果您确实在代码中键入了符号,这很好(毕竟您只有有限数量的代码),但不要在动态生成的字符串或用户输入字符串上调用_sym,因为这将打开一扇通向可能不断增加的数目的大门不要这样做:
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
。(不确定1.8.7,因为我还没有尝试过,但我真的希望它是固定的。)解决方法很愚蠢,需要创建一个局部变量。你不必使用本地的,只要创建一个
这就是为什么我非常喜欢ruby语言,但不尊重MRI的原因。在处理大量ActiveRecord对象数组时,请非常小心。。。在循环中处理这些对象时,如果在每次迭代中使用ActiveRecord加载它们的相关对象时有很多、属于等等,那么内存使用会增长很多,因为属于数组的每个对象都会增长 以下技术对我们帮助很大(简化示例): 在上面的代码中,“cloned_student”是增长的对象,但由于它在每次迭代结束时都被“null化”,因此对于大量的学生来说这不是问题。如果我们不进行“克隆”,循环变量“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的内存来处理相同数量的数据…测量并检测代码中哪些部分正在创建导致内存使用量增加的对象。改进和修改代码,然后再次测量。有时,您使用的gem或库会占用大量内存,并创建大量对象 有很多工具,例如,允许您检查对象(包括散列和数组中的对象)的内存大小 示例#1:MemorySize.of 示例2:MemoryUtils.profile 代码 输出:
您还可以尝试使用ruby prof和内存分析器。最好测试和实验不同版本的代码,以便测量每个版本的内存使用情况和性能。这将允许您检查您的优化是否真的有效。您通常在开发/测试模式下使用这些工具,并在生产中关闭它们。是的,这是一个众所周知但尚未修复的错误。我很确定这与MRI解释器所做的优化有关,以避免在作用域中没有方法局部变量的情况下创建局部堆栈帧。在Ruby 1.8.7中似乎不再发生这种情况。在这种情况下,取消对blob的引用并调用GC.start可以释放内存。RMagic 2.2应该在内部处理这个问题(但仍然没有nil+GC那么有效)Dave,我对你的评论很好奇。所以,如果你在Ruby中有一个对象的大实例,你可以这样做让Ruby发布它:a=nil GC.start有点像Erlang的atoms-neat。我没有意识到这一点。在Ruby 2.2中,符号将被垃圾收集。Alex,这是一个很好的技巧,我已经测试过,它使我们的两个进程减少到15mn。但我不确定一件事,为什么student不释放,因为它在每次迭代中都应该获得新值?student对象不释放,因为它仍然属于一个student数组,即使它是一个循环变量-它在每次迭代中没有获得新值,而是用作指向数组中项目的指针(排序)。因此,如果student的大小增加,数组的大小也会增加。这就是为什么如果你克隆了student,你实际上是在处理一个单独的对象,这个对象在每次迭代中都会被释放。我们已经处理了一段时间相同的长时间运行的内存占用后台作业问题。这也许能解决问题!谢谢Alex,我也遇到了这个问题。在有无最终nil的情况下进行测试-结果相同。看来你所需要的就是克隆这个对象。我认为最重要的是:知道你为什么要用那么多的内存,以及内存在哪里。也许有一个工具可以做到这一点。
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
$ gem install busy-administrator
require 'busy-administrator'
data = BusyAdministrator::ExampleGenerator.generate_string_with_specified_memory_size(10.mebibytes)
puts BusyAdministrator::MemorySize.of(data)
# => 10 MiB
require 'busy-administrator'
results = BusyAdministrator::MemoryUtils.profile(gc_enabled: false) do |analyzer|
BusyAdministrator::ExampleGenerator.generate_string_with_specified_memory_size(10.mebibytes)
end
BusyAdministrator::Display.debug(results)
{
memory_usage:
{
before: 12 MiB
after: 22 MiB
diff: 10 MiB
}
total_time: 0.406452
gc:
{
count: 0
enabled: false
}
specific:
{
}
object_count: 151
general:
{
String: 10 MiB
Hash: 8 KiB
BusyAdministrator::MemorySize: 0 Bytes
Process::Status: 0 Bytes
IO: 432 Bytes
Array: 326 KiB
Proc: 72 Bytes
RubyVM::Env: 96 Bytes
Time: 176 Bytes
Enumerator: 80 Bytes
}
}