RubyでGCしても直ちにRSSが減るわけではないというお話

先に結論

  • GCをしても、RSSが直ちに減るわけではない
  • C言語のレイヤーで、free しても次の malloc に割り当てるためにメモリを手放さない実装があるのが原因らしい

計測してみた

適当な長さのStringを10000回作って、RSSと、ObjectSpace.memsize_of_all を計測する

require 'objspace'

puts ",ObjectSpace.memsize_of_all,RSS"
1.upto(10000) do |i|
  a = "10"*10000
  a += '123'
  rss = `ps -o rss= -p #{Process.pid}`.to_i * 0.001
  memsize_of_all = ObjectSpace.memsize_of_all * 0.001 * 0.001
  puts "#{i},#{rss},#{memsize_of_all}"
end

結果

以下のグラフの通り

memsize_of_all は数ループに1回GCされているが、実際にプロセスがメモリを手放すタイミングはもっとゆっくりしている

f:id:kasei_san:20180517221213p:plain

単位はMB

なんでGCしても、RSSはすぐに減らないの?

stackoverflowだけれど納得感のある解説を見つけた

Cレベルの話をすると、free を呼んでもOSにメモリを返さず、次に malloc した際に再割当するような実装も多く存在します。 ということで、一般的にはメモリの解放によっても占有メモリが減らない前提でプログラミングすることになろうかと思います。

2018/05/18 追記

会社のひとから、上記のコメントは Conservative GC と混同しているのではないか? とご意見頂いた

参考リンク