在使数组无效后,不会释放Ruby内存

时间:2015-11-05 16:53:57

标签: ruby-on-rails arrays ruby memory

我认为通过以下示例更容易显示我的问题:

我使用纯Ruby 2.2.2,但Rails 4.2.2也是如此。

require 'bigdecimal'
x = 20000000

第一种情况:

big_number = BigDecimal.new(500)
x.times {|i| @array << big_number }

第二种情况:

(x/20).times {|i| @array << BigDecimal.new(500) }

在对象@array和垃圾收集器执行无效后,在第一种情况下,内存使用返回初始状态。 在第二种情况下,永远不会释放内存。

更大的内存使用量(20x)可以通过在第一种情况下使用引用和在第二种情况下使用新对象来解释,但为什么在第二种情况下不释放内存?

完整的例子:

def show_memory
  size = `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1]
  puts "#{size}"
end

# Starts with the same 14 Mb on IRB
require 'bigdecimal'
x = 20000000

puts "With variable"
5.times {
  array = []
  hash = BigDecimal.new(500)
  x.times {|i| array << hash }
  show_memory
  array = nil
  GC.start
  show_memory
}

puts "\nWithout variable"
y = x/20
5.times {
  array = []
  y.times {|i| array << BigDecimal.new(500) }
  show_memory
  array = nil
  GC.start
  show_memory
}

输出:

With variable
165448
9460
165564
9464
165564
9464
165564
9464
165564
9464

Without variable
158068
150388
167016
167016
167016
167016
167016
167016
167016
167016

P.S。编辑问题以使用Yacine的评论。

2 个答案:

答案 0 :(得分:1)

我尝试了你的代码,稍加修改后就有了实时内存:

def show_memory
  size = `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1]
  puts "#{size}"
end

# Starts with the same 14 Mb on IRB
require 'bigdecimal'
x = 2000000
@array = []

show_memory

x.times {|i| @array << BigDecimal.new(500) }

show_memory

# The used memory is now about 760Mb
@array = nil
GC.start
show_memory

在第一段代码中,我得到:

7552 309572 285792

关于我得到的第二个代码:

7232 768964 647928

所以他们两个都没有记忆。我想Ruby VM处理他自己的内存管理就像Java一样,只是为你的应用程序扩大堆,然后不会禁止它。

可以肯定的是,我再次更新了代码,写了这个:

def show_memory
  size = `ps ax -o pid,rss | grep -E "^[[:space:]]*#{$$}"`.strip.split.map(&:to_i)[1]
  puts "#{size}"
end

# Starts with the same 14 Mb on IRB
require 'bigdecimal'
x = 2000000

50.times {
  @array = []
  x.times {|i| @array << BigDecimal.new(500) }
  show_memory
  @array = nil
  GC.start
  show_memory
}

内存增加到~600Mb然后永远保持在这个水平,证明没有内存泄漏,而是一些核心内存管理:

内存值@array loaded / @ array nil:

312508
278500
348688
331172
361208
359160
391664
386544
415280
414264
444316
441244
458152
448744
466632
464584
480320
479192
498196
495132
514208
514208
530620
529596
553976
551284
567784
565736
582284
579212
595224
589080
616952
607820
613300
594588
602404
586776
602612
601588
618032
602404
618440
594628
609512
593884
609724
590000
606972
591352
607236
590584
606276
590648
606284
588608
604272
587620
607452
591824
608428
590752
606852
588152
603820
583072
604672
598596
599892
584264
591904
575252
592588
592588
608808
593188
609244
592592
609572
593236
615500
598848
615452
597776
614124
587240
608284
583448
600004
584376
600052
581352
598748
583128
599984
584356
...

正如你所看到的,它会增长并且......保持在600Mb。

如果你计划拥有大量的ruby进程但没有那么多的RAM来处理它们,那么这仍然是一个问题。也许这个“ceil”可以配置,但我没有想法。

希望它会对你有所帮助!

答案 1 :(得分:1)

需要区分内存释放的含义:

    收集垃圾后
  • VM内未使用的内存

    在两个示例中,垃圾收集器实际上都是免费的以前分配的内存。您可以使用ObjectSpace.count_objects.count_objects_size方法来检查自己, .memsize_of_all等 您可能会看到T_DATA个对象的增加,并在GC.start之后回退到之前的值。这同样适用于总消耗的内存。

  • 从VM到底层操作系统的未使用内存

    当发生这种情况时仍然有点神秘,据我所知,标准Ruby中没有办法手动调用它。 Ruby的VM(YARV)为以前的分配保留了以前GCed的可用内存,更有效地再次从OS获取。

    顺便说一下。第一个示例以相同的方式工作,并被错误地识别为返回初始状态。与第一种情况不同的是,只创建了一个对象big_number = BigDecimal.new(500),在第二种情况下创建了 20百万个对象。即使在第一个示例中,您可能会看到小而明显的RSS内存增加,大致相当于每个元素引用同一对象的数组大小。

此外,两者都是实现和操作系统特定的。