你有什么策略来保持低内存使用率?

时间:2008-09-27 21:41:33

标签: ruby memory-management garbage-collection

Ruby真正渴望内存 - 但也值得每一位。

你如何保持低内存使用率?你是否避免使用较大的字符串并使用较小的数组/哈希值,或者关注你并让垃圾收集器完成工作是没有问题的?

编辑:我发现了一篇关于这个主题here的好文章 - 旧的,但仍然很有趣。

17 个答案:

答案 0 :(得分:9)

我发现了Phusion的Ruby企业版(主线Ruby的一个分叉,其垃圾收集得到了很大改进),以便在内存使用方面产生巨大的变化......而且,它们使得它非常容易安装(并且可以删除) ,如果你发现需要)。

您可以在their website上找到更多信息并下载。

答案 1 :(得分:8)

我真的认为这不重要。 只有在需要时才能使代码具有较低的可读性以提高内存消耗。根据需要,我的意思是对性能配置文件有特定情况特定指标表示任何更改都会解决问题。

如果你的应用程序内存将成为限制因素,那么Ruby可能不是最佳选择。也就是说,我发现我的Rails应用程序通常每个Mongrel实例消耗大约40-60mb的RAM。在方案中,这不是很多。

您可以使用JRuby在JVM上运行应用程序 - Ruby VM当前不像JVM那样用于内存管理和垃圾回收。 1.9版本增加了许多改进,还有正在开发的替代VM。

答案 2 :(得分:6)

  1. 选择有效表示的日期结构,可以很好地扩展,并按照您的需要进行操作。
  2. 使用可以使用高效数据结构而非臃肿但更容易使用的算法。
  3. 看看别的地方。 Ruby有一个C桥,在C中比在Ruby中更容易记忆。

答案 3 :(得分:6)

Ruby开发人员非常幸运,因为他们不必自己管理内存。

请注意ruby会分配对象,例如

这样的简单对象
100.times{ 'foo' }

分配100个字符串对象(字符串是可变的,每个版本都需要自己的内存分配)。

确保如果您使用的库分配了大量对象,则其他替代方案不可用,您的选择值得支付垃圾收集器成本。 (你可能没有很多请求,或者每个请求可能不关心几十毫秒)。

创建哈希对象确实分配的不仅仅是一个对象,例如

{'joe' => 'male', 'jane' => 'female'}

不分配1个对象而是7.(一个哈希,4个字符串+2个密钥字符串)

如果您可以使用符号键,因为它们不会被垃圾收集。但是因为它们不会被垃圾收集,所以你要确保不要使用完全动态的密钥,比如将用户名转换为符号,否则你会“泄漏”内存。

示例:在应用中的某个位置,您可以在用户名上应用to_sym,如:

hash[current_user.name.to_sym] = something

如果您有数百名用户,那可能没问题,但如果您拥有一百万用户,会发生什么?以下是数字:

ruby-1.9.2-head >
# Current memory usage : 6608K
# Now, add one million randomly generated short symbols
ruby-1.9.2-head > 1000000.times { (Time.now.to_f.to_s).to_sym }

# Current memory usage : 153M, even after a Garbage collector run.

# Now, imagine if symbols are just 20x longer than that ?
ruby-1.9.2-head > 1000000.times { (Time.now.to_f.to_s * 20).to_sym }
# Current memory usage : 501M

请注意,以前永远不会在符号或检查参数中转换非受控参数,这很容易导致拒绝服务。

还要记住避免嵌套循环超过三层,因为它会使维护变得困难。将循环和函数的嵌套限制在三个或更少的级别是保持代码性能的一个好的经验法则。

以下是一些关于以下方面的链接:

http://merbist.com

http://blog.monitis.com

答案 4 :(得分:4)

  1. 部署Rails / Rack webapp时,请使用REE或其他一些写时友好的口译员。
  2. 调整垃圾收集器(例如,参见https://www.engineyard.com/blog/tuning-the-garbage-collector-with-ruby-1-9-2
  3. 尝试减少您使用的外部库/宝石的数量,因为其他代码使用内存。
  4. 如果您的应用程序中有一部分内存密集,那么可能值得在C扩展中重写它或通过调用其他/更快/更好的优化程序来完成它(如果您必须处理大量文本数据) ,也许您可​​以通过调用grep,awk,sed等来替换该代码。)

答案 5 :(得分:3)

我不是一个红宝石开发者,但我认为一些技巧和方法适用于任何语言:

使用适合作业的最小尺寸变量
不使用时销毁和关闭变量和连接
但是,如果您有一个对象,则需要多次使用,请考虑将其保留在范围内 任何带有大字符串dp操作的循环都会在较小的字符串上工作,然后附加到较大的字符串

使用体面(try catch finally)错误处理来确保对象和连接已关闭

处理数据集时,只返回必要的最小值

答案 6 :(得分:2)

除极端情况外,内存使用情况不用担心。您花费在减少内存使用量上的时间将购买 LOT 千兆字节。

答案 7 :(得分:2)

看看Small Memory Software - Patterns for Systems with Limited Memory。你没有指定什么样的内存约束,但我假设RAM。虽然不是特定于Ruby的,但我认为你会在本书中找到一些有用的想法 - 模式包括RAM,ROM和二级存储,并分为小数据结构,内存分配,压缩,二级存储和小型的主要技术。架构。

答案 8 :(得分:2)

我们曾经唯一值得担心的事情是RMagick。

解决方案是确保您使用RMagick版本2,并在使用完图片后致电Image#destroy!

答案 9 :(得分:2)

避免这样的代码:

str = ''
veryLargeArray.each do |foo|
  str += foo
  # but str << foo is fine (read update below)
end

将创建每个中间字符串值作为String对象,然后在下一次迭代时删除其唯一的引用。这使得记忆中的大量长得越来越长的字符串必须被垃圾收集。

相反,请使用Array#join

str = veryLargeArray.join('')

这在C中非常有效地实现,不会产生字符串创建开销。

更新:Jonas在下面的评论中是正确的。我的警告适用于+=但不适用<<

答案 10 :(得分:1)

将malloc(3)实现替换为jemalloc将立即将内存消耗降低30%。我已经创造了'jemalloc'宝石来实现这一目标。

答案 11 :(得分:1)

如果可能,请使用数组而不是其他数据结构。当整数可以做时,尽量不要使用浮点数。

使用gem / library方法时要小心。它们可能不是内存优化的。例如,Ruby PG :: Result类有一个未优化的方法'values'。它将使用大量额外的内存。我还没有报告这个。

答案 12 :(得分:1)

要记住的是物体的生命周期。如果你的对象没有传递那么多,那么垃圾收集器最终会启动并释放它们。但是,如果您继续引用它们,垃圾收集器可能需要一些周期来释放它们。在Ruby 1.8中尤其如此,垃圾收集器使用标记和扫描技术的不良实现。

当您尝试应用一些“设计模式”(例如装饰器)时,您可能会遇到这种情况,这些模式将对象长时间保留在内存中。在孤立地尝试示例时可能并不明显,但在现实世界的应用程序中,同时创建了数千个对象,内存增长的成本将会非常高。

答案 13 :(得分:1)

我正在使用Python,但我猜这些策略是相似的。

我尝试使用小函数/方法,以便在返回调用者时自动收集局部变量。

在较大的函数/方法中,当不再需要大型临时对象(如列表)时,我会明确删除它们。尽早关闭资源也可能有所帮助。

答案 14 :(得分:1)

我在Ruby方面很陌生,但到目前为止,我还没有发现在这方面做任何特别的事情(也就是说,除了我作为一般程序员所做的事情之外)。也许这是因为内存比认真优化内存要便宜(我的Ruby代码在4-12 GB RAM的机器上运行)。这可能也是因为我使用它的工作不会长时间运行(即它将取决于你的应用程序)。

答案 15 :(得分:0)

不要使用很多符号,它们会留在内存中直到进程被杀死..这是因为符号永远不会被垃圾收集。

答案 16 :(得分:0)

我试着保持阵列和放大器列表&amp;数据集尽可能小。单个对象并不重要,因为在大多数现代语言中,创建和垃圾收集速度非常快。

在必须从数据库中读取某种巨大数据集的情况下,请确保以正向/唯一方式读取并以小位处理它而不是首先将所有内容加载到内存中。