オブジェクト指向言語でのメモリの仕組み


記事を書こうと思った理由

仕事でspring bootを使っており、その肝であるDI(Dependency Injection)では、
singletonデザインパターンが採用されており、デフォルトで1つしかインスタンスが生成されないという。
ネットで調べてもspring bootで状態を持つような実装をしてるとバグるみたいな記事がわんさか出てきた。
色々考えると、あれ???インスタンスは1個しか作られないけど、1つのAPIに対して同時リクエストとかされると、大丈夫か?どうなってんだ???と思ったりした。
たぶんメモリ配置がどこに?どの期間?というのが理解できていないためだと思ったので改めて整理した。

メモリの仕組み(Javaベース)

まずは、メモリ領域
・静的領域
・ヒープ領域
・スタック領域

続いて、メモリへ展開するのも(だいたいなので、他にもありそうな)
1. コード情報(クラス本体)
2. グローバル変数
3. インスタンス
4. インスタンス変数(コンストラクタとかで指定)
5. ローカル変数
6. 各メソッドの引数
7. 戻り値

それぞれを表でまとめたのがこちら

ということで、改めて考えると、「状態を持ってはいけない」というのはインスタンス変数を持つような実装にすると、
バグるということで、5、6、7はリクエストのスレッド情報としてスタック領域に展開されるのでバグるとかの心配は無いですね。

シングルトンを改めて考える

改めてシングルトンのデザインパターン眺めても、スッと頭に入ります。
グローバル変数として1つだけインスタンスを作製し、それはprivate変数になっているので再度作成されることもない。
getInstance()メソッドの呼び出すことで、先程、ヒープ領域に展開したインスタンスのアドレスをリターンしている。
spring bootのアノテーションで@Bean@Controller@Service@Componentを各クラスに付与してあげると、内部ではこんな感じの動きになっていると思われる。

singlton.java
public class Singlton{
private static Singlton singlton = new Singlton();
    private Singlton(){}
    public static Singlton getInstance(){
        return singlton;
    }
}

おまけ NARUTOの世界で考えてみた

●NARUTOで例えると・・
ナルト(本体):コード情報
→色んな術(メソッド)を持っている。
ナルト(影分身体):オブジェクト
→こなす仕事はそれぞれ異なる。
定期的に分身体を解除しないとナルト本体がチャクラ切れになる(発生するのはメモリリーク、定期的にオブジェクトを消すのはガベージコレクション)。
ナルト(影分身)の行動:ローカル変数など
→手裏剣投げるとか、術使うとかの行動。

結論:NARUTOとは・・・object ninja!!!