【Ruby】Objectのメモリっていつ解放されるの？

僕はRubyからプログラミングを書くようになった現代っ子エンジニアですが、Rubyについて学べば学ぶほど、裏でこんなことが起きてるんだと驚く日々です！

ふと思ったんですが
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150607/cb36eb8d-91ad-5bda-22a3-da9fce4c43a9.png)
もちろん、ブロック内でしか参照できないので、ブロックの実行が終わるごとに消えそうです
するとじゃあ、`消える`って何なの？ってなります
OSには詳しくないですが、多分メモリ上から消えるってことですよね
ってことは多分、`メモリに書き込む`と`メモリから消す`という動作があるのかなーという予想🪐

Rubyの代入が`object_id`渡しってのはご存知の通りとして、
ってことは多分オブジェクトの生成の中で`object_idの確定`と`メモリの確保(書き込み)`があるんだろうなーという予想🪐🪐
一方で、`メモリの解放`はいつ行われてるんだろう？？？ブロック終了時に毎回行ってるんかな？

ちょっとRubyのメモリ管理について調べます
~~半信半疑で書いてるので、半信半疑で読んでね😈~~

CRuby前提で書くのでご了承

```sh
ruby -v
# => ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
```

# Rubyのメモリ解放の仕組み（Garbage Collection）
Rubyでは[世代別ガーベージコレクション](https://ja.wikipedia.org/wiki/%E4%B8%96%E4%BB%A3%E5%88%A5%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)という仕組みが使われているらしいです
> Ruby (2.1以降) などに採用されている

ガーベージコレクションは実行されると、使わなくなったオブジェクト（メモリ領域？）を見つけて、メモリを解放してくれる仕組み
オブジェクトは`young`と`old`のどっちかに判定されて、主に`young`のオブジェクトを解放する

Rubyでは`youngの解放`を`マイナーGC`って呼び、`young/oldの解放`を`フルGC`って呼びます

[GC](https://docs.ruby-lang.org/ja/latest/class/GC.html)（ガーベージコレクション実行のモジュール）を見てみると

> マイナーGC - 新しいオブジェクト(前回のGCの後に生成されたオブジェクト) のみを対象としたGC
> マイナーGCでGCされなかったオブジェクトはoldobjectと呼ぶ

つまり、前回のマイナーGCから今回のマイナーGCまでに作成されたオブジェクトが、`oldとして残る` or `解放される`ってことなんですね

> RubyのフルGCは oldobject の個数が閾値 old_objects_limit を越えるごとに実行される
> malloc_increase_bytes が malloc_increase_bytes_limits を越えるとマイナーGCが起動される
> oldmalloc_increase_bytes が oldmalloc_increase_bytes_limits を越えるとフルGCが起動される

なるほど、ブロックごとにメモリ解放ではなくて、オブジェクトの個数や、メモリ使用量(byte)が閾値を超えたら解放してるのか
やってみよう


# GC.statでオブジェクトの解放を確認
自動でGCが行われない程度に、3000行の配列を生成して、マイナーGCを実行
ポイントは、`a`に代入してることで、GCによって回収されないので、解放されないことです

```test.rb
# ruby test.rb で実行
p GC.stat
a = Array.new(3000) do
  'x'
end
GC.start(full_mark: false) # マイナーGC実行
p GC.stat
```

↓一部抜粋

| 前 | 後　|
| --- | ---- |
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150607/6dbcd40f-33e8-9655-d87d-061630eb2b7f.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150607/07f9f77c-5b87-2e51-d551-0fea63d409ac.png) |

↓各項目の参考 : https://gist.github.com/sonots/71277ef3f9b53fa87862

```rb
{
  :heap_live_slots=>, # 生きてるスロット（オブジェクト）の数
  :heap_free_slots=>, # 空いてるスロットの数
  :minor_gc_count=>, # マイナーGCの実行回数
  :old_objects=>, # マイナーGCで解放されないオブジェクトの数
}
```

今回は3000個ほどのオブジェクトが解放されない(`old`に判定される)ので、`old_objects`が3000件ぐらい残ってるのがわかりますね。（実際には内部で色々実行されるので、値は毎回違います）
内部の何かしらが解放されて、生きてるスロットが減って、空いてるスロットが増えてますね

代入しないパターンもやってみましょう

```test.rb
p GC.stat
Array.new(3000) do
  'x'
end
GC.start(full_mark: false) # マイナーGC実行
p GC.stat
```

| 前 | 後　|
| --- | ---- |
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150607/1a919a50-be03-1316-58a9-d4d2b48dd54c.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150607/9684194f-be41-76d7-4976-c5decf00fbc1.png) |

今回は3000個解放されるので、`old_objects`がさっきほど増えてないですね
また、空いてるスロットも代入の時より3000件ほど多くて、生きてるスロットは3000件ほど少ないです

# 所感
本題がちょっとミスリードっぽくなってしまいましたね、、`メモリ解放`じゃなくて`オブジェクト解放`になりました（つまり同じかもですが、何とも😒）
これが理解できると、なぜRailsのメモリは肥大化し続けるのか以下のquora回答の意味もわかると思います。
[プロダクションの Rails サーバーの利用メモリがひたすら増加していくような挙動を観測したとき、どう対応するのがよいですか？](https://jp.quora.com/purodakushon-no-Rails-sa-ba-no-riyou-memori-ga-hitasura-zouka-shi-tei-ku-you-na-kyodou-wo-kansoku-shita-toki-dou-taiou-suru-no-ga-yoi-desu-ka)

まあでもここまで書いておいて何ですが、こんなこと考えて開発したくないと思いました😈
C言語時代にメモリ解放を頑張って書いてた人には勤労感謝したい