Hatena::ブログ(Diary)

やさしいデスマーチ このページをアンテナに追加 RSSフィード

札幌のWebエンジニアの綴る日常と開発の日々。
GoogleAppEngine/slim3/Django/NetBeans/Swing/JavaFXを中心にお届け。

2007-02-11 JVMのメモリ管理

JVMのメモリ管理

| 23:42 | JVMのメモリ管理を含むブックマーク

ここで頭の中を整理する為にJVMのメモリ構造に関して触れておきます。

JVMの管理するメモリ領域には大きく2つの区画があり、それぞれスタック・ヒープエリアと呼ばれます。

f:id:Strada:20070211233225g:image

ヒープ

ヒープエリアは主にインスタンスを管理するメモリ領域です。

ヒープエリアにはメソッドエリアと呼ばれるクラスの情報やメソッドの情報などの静的な情報を格納する区域があり、ここはGCの対象とはなりません。

インスタンスが生成される時はメソッドエリアに格納されているクラス情報を雛形として実データが作成されます。

スタック

スタックは主にプログラムの実行を制御する為の領域で処理(命令・メソッド)が格納されます。

スタックに詰まれた処理は上から順番に実行され、実行済みの処理は破棄されていきます。

これはいわゆるLIFO(Last-In-First-Out: 後入先出) のスタック(Stack:待ち行列)と呼ばれる構造です。

単純に言えばソースコードをmainメソッドの先頭より1行1行実行していくイメージです。

変数の格納場所

インスタンスはヒープに格納されていますが、処理の中に含まれる変数はスタックに格納されています。

つまり、ローカル変数(メソッドの中で宣言される変数、メソッドの引数を含む)はスタックに格納されます。

もし、mainメソッドのみで完結してインスタンスを生成しないプログラムを書いたとすれば、ヒープにインスタンスは生成されません。

単純にスタックにmainメソッドの命令が積まれて順番に実行されていきます。

この中でローカル変数によるプリミティブ型の演算を行ったとしましょう。

    public static void main(String[] args)
    {
        int x = 10;
        int y = 20;
        int z = x + y;
    }

このプログラムの変数をスタックに積み上げると次のようになります。

f:id:Strada:20070211233222g:image

このようにローカル変数はスタックの中で確保され、メソッドの終了時には自動的に破棄されていきます。

これは参照型変数であっても変わりません。

    public static void main(String[] args)
    {
        HelloWorld hello = new HelloWorld();
        hello.helloWorld();
    }

インスタンスはヒープに確保されますが、参照型変数はスタックにプリミティブ型変数と同じように存在します。

つまり、プリミティブ型変数と参照型変数の違いはスタックに実データが格納されているか、ヒープに確保されたインスタンスを参照しているか、という点です。

f:id:Strada:20070211233220g:image

Javaのプリミティブ型とラッパークラス

このようにJavaではローカル変数(メソッド変数)はインスタンスを生成せずに直接スタックに展開されます。

Javaではプログラムの最小単位はクラスであり、原則として全ての値はクラスのインスタンスを生成することで実現されると学んできましたが、この事はインスタンスを生成しない(クラスでもない)事になります。

実はオブジェクト指向言語(特定の言語ではなく概念的な言語)としてみた場合、Integerというクラスのインスタンスを使用するべきとなります。

        // Integer型変数xの宣言とインスタンスの生成
        // 変数xはスタックに確保され、Integerのインスタンスを参照している状態
        Integer x = new Integer();

ところが、プログラムで全ての整数を扱う箇所にこのような記述をしては非常に冗長となります。

また、初期の計算機と比べメモリの量はほとんど気にせず使用できるようになったとは言えども、32bitのデータを格納する為に大量のメモリを消費するのは効率が悪いと言えるでしょう。

計算処理であっても次のようにクラスのメソッドとして定義することになります。

        Integr x = new Integer();
        Integr y = new Integer();
        x.add(y);

注) コンパイルは通りません。

これらの冗長な書き方はオブジェクト指向言語としてみた場合は正しいと言えます。

しかし、次のように書けた方が理解しやすいでしょう。

        int x = 10;
        int y = 20;
        x = x + y;

そこでJavaではオブジェクト指向言語の特徴を取り入れつつも、プリミティブ型(基本型)を上記のように記述できるなど、上手い所で妥協しています(このような表記方法はC言語を始め、ほとんどのプログラミング言語で採用している式という理由もあるでしょう)。

ただし、オブジェクトとして整数型を扱えないと不便な場合もあり、そのような場合にjava.lang.Integerクラスを使用する、となっています。

このIntegerクラスなどはプリミティブ・ラッパー・クラスと呼ばれ、プリミティブ型を包み込む(ラップ)クラスです。

学者的な視点でJavaを見ると「オブジェクト指向言語ではない」のかもしれませんが、現場でプログラムを書く人にとっては最善ではなくとも最良のところで仕様が決められています。

尚、プリミティブ型であってもインスタンス変数はインスタンスの内部に領域が確保されますが、これに関しては後日解説しましょう。

トラックバック - http://d.hatena.ne.jp/shuji_w6e/20070211/1171204927