与.map相比,.each会有不同的GC影响吗?

时间:2016-01-21 01:42:59

标签: ruby-on-rails ruby loops garbage-collection iterator

这两种垃圾中的一种收集方式不同,还是一样?

array_of_strings.each do |str|
  MailerClass.some_template(str).deliver_later
end

VS

array_of_strings.map do |str|
  MailerClass.some_template(str).deliver_later
end

我已经格式化了邮件对象和任务队列的问题,因为这是我最近一直在处理的问题,但是如果这会使答案过于复杂,那么我可以将其改为更通用。

背景,相关:

  

我认为.map会返回一个指向每个对象的对象   已创建的对象,而.each则不会。 [这似乎是   不是这样的]

     

我有一台工作机器泄漏内存直到崩溃 - 所有   发生在这个伪代码示例的一个事件中   中央行动。当我将其从.each更改为.map时,则将其更改为type CoreOperators<T> = { map: <R>(f: (item: T) => R) => Observable<R>; scan: <R>(f: (prev: R, next: T) => R) => Observable<R>; }; type Observable<T> = CoreOperators<T> & { observableSpecificMethod: (f: Function) => Observable<T> };   内存泄漏已停止,但我不知道GC是如何发生的   关于这些方法。

2 个答案:

答案 0 :(得分:1)

由于map返回使用array_of_strings的每个成员计算块的结果的数组,因此至少会有一个额外的对象被收集。

答案 1 :(得分:1)

根据您使用的确切Ruby实现,Enumerable#map的实现可以用Ruby,Smalltalk,RPython,ECMAScript,C#,Java,Go,Objective-C,C ++,C或任意数量的其他语言,但其工作方式总是如下:

module Enumerable
  def map
    return enum_for(__method__) unless block_given?
    [].tap {|result| each {|el| result << yield el }}
  end
end

map 必须调用each,根本无法进行迭代。因此,它将分配完全相同的对象,each将分配加上附加结果Array,即恰好比each多一个对象。

实际上,许多Ruby实现使用Enumerable#map覆盖Array#map,这比通用Enumerable版本更有效。它们通常直接遍历Array元素而不调用each,有点像这样:

class Array
  def map
    return enum_for(__method__) unless block_given?

    result = []

    i = -1
    while i += 1 <= size
      result << yield @__entries__[i]
    end
  end
end

这比通用Enumerable#map更有效,因为它使用了有关如何迭代Array的特殊知识,以避免方法调用开销each。但是请注意,each将以完全相同的方式实现,除非实现者非常愚蠢,因此最终结果是相同的:map仍然会分配超过{{1}的一个对象它只是保存一个方法调用。

请注意,由于您忽略了each的返回值,因此额外对象立即有资格进行垃圾回收。但是,在此之前,它保存对map方法返回的所有对象的引用,无论它们是什么。

因此,两段代码之间的差异是:

  • 使用deliver_latereach的返回值将被忽略,并有资格立即进行垃圾回收
  • deliver_later一起,分配了一个额外的map,它保留了对块内所有Array调用的返回值的引用,从而使这些对象保持活动状态,直到deliver_later的调用返回之后,map及其引用的所有对象将同时符合垃圾回收的条件