• ベストアンサー

Javaのメモリの管理

なんか基本的なことなんですけど、ちゃんと把握していないなーと思って質問します。 staticメソッドを使用してしまうと、メモリ上に残ってしまうからあまり使うななどとよく言うと思います。これに対して、staticでないメソッドは使用されている間はstaticと同じように管理され、使用後に開放対象となるということなのか。というか、非staticメソッドは、2箇所から同時にアクセスされたらメソッド2つ分のメモリを使用するのか。その前に、staticメソッドであっても、同時に2箇所からアクセスされたら2つ分のメモリを消費するはずではないのか。 サーブレットでは、アクセスのあったサーブレットクラスがインスタンス化され、そのインスタンスが何度も使い回される、との記述を見たが、複数のアクセスがあったときに同時に処理ができるが、これは実質メモリ上に複数のメソッドが存在することにならないのか… ロードの際にはクラス単位で読み込まれるのか、メソッド単位なのか。staticメソッドと非staticメソッドが混在しているクラスでは、読込み、開放はメソッド単位で行っているのか。 などの疑問を持っています。以上、まとまりのない疑問ですが、なんとなく私がわからない部分はわかっていただいたのではないかと思います。この辺の話について、よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
noname#49664
noname#49664
回答No.1

私がわかっている範囲内ですが、ざっとJava仮想マシン内のメモリ状況について整理してみます。 まずオブジェクトを保管するためのメモリ領域ですが、これは「メソッドエリア」と「ヒープ」の2つがあります。メソッドエリアはクラスを保管する場所であり、ヒープはインスタンスを保管する場所です。これとは別に、実行のためのエリアとして「Javaスタック」と呼ばれる領域がスレッドごとに用意されます。これが基本です。 Javaでプログラムが実行される(すなわち、新たなスレッドがたてられる)と、JVMは、まずそのスレッドが使用するためのJavaスタックをメモリ内に確保します。そして、メソッド等がコールされる度に「フレーム」と呼ばれる領域を作成し、そこに必要な情報を保管します。  実行するスレッド内からオブジェクトが利用されようとすると、JVMはまず使用するクラスを「メソッドエリア」にロードします。そしてインスタンスを作成する際には、メソッドエリアにロードされたクラスをヒープにコピーし、インスタンスを作成します。  プログラムからメソッドなどを利用する場合には、Javaスタック内にそのメソッド用のフレームを確保し、そこにローカル変数の情報やオペランドスタックなどを保管します。 従って、JVMの中では「クラス=メソッドエリア」「インスタンス=ヒープ」「利用するメソッド内で使用する変数類=実行するスレッドに対応するJavaスタック内のメソッドに対応するフレーム」という形でそれぞれ保管がされています。以上が、だいたいの概略です。 >staticメソッドを使用してしまうと、メモリ上に残ってしまうからあまり使うななどとよく言うと思います。 これは、要するに「staticなメソッドやフィールドはガーベージコレクタの対象とならない」ということがいいたいのでしょう。staticでないものは使用後にgcによってメモリから破棄されますから随時メモリはクリアされます。  が、反面、staticでないものはnewによりインスタンスを作成する必要があり、これは実はけっこうなCPUへの負荷になります。従って、ごくわずかばかりのメモリ消費を気にするために不必要にstaticでないものを増やすと逆にCPUへの負荷を余計にかけてしまうことになるため、あまりよいとは思えません。  また、そもそもstaticなものはインスタンスを作りませんから、でっかいインスタンスを作って不要になったら消すより、最初から作らないstaticの方がメモリに優しいでしょう。 >これに対して、staticでないメソッドは使用されている間はstaticと同じように管理され、使用後に開放対象となるということなのか。 Javaは、メソッド単位でロードをしていません。クラス単位です。まず、その点を勘違いしないようにしてください。で、おっしゃるようにstaticでないものは不要になった時点で(適当な時期に)ガーベージコレクタによりインスタンスが消去され、メソッドもメモリから消えます。 >その前に、staticメソッドであっても、同時に2箇所からアクセスされたら2つ分のメモリを消費するはずではないのか。 いいえ。staticなメソッドはクラス内に残るため、常に同一のものが参照されます。 >複数のアクセスがあったときに同時に処理ができるが、これは実質メモリ上に複数のメソッドが存在することにならないのか… ローカル変数の内容については各スレッドのフレーム内に保持されますからアクセスが増えればメモリを消費しますが、staticなメソッドを利用する場合には各フレームにはstaticなメソッドへの参照だけがスタックされるだけで複数のstaticメソッドが積み上げられることはありません。  非staticなメソッドは、newされただけインスタンスがヒープに作成されるため、同じメソッドが複数存在することはあります。 >ロードの際にはクラス単位で読み込まれるのか、メソッド単位なのか。staticメソッドと非staticメソッドが混在しているクラスでは、読込み、開放はメソッド単位で行っているのか。 クラス単位です。 staticと非staticが混在している場合、まずクラスがメソッドエリアにロードされた後、newした段階でヒープにインスタンスが作成されます。このとき、staticなものはクラス内に残されます。  インスタンスは使用後に必要に応じてgcによって破棄されます。クラスは、スレッドが終了するまで保持されます。従って、staticメソッドはクラス内に保持されるため、同様にスレッドが終了するまでメモリ内に置かれます。 なお、私もまだまだ勉強不足なところがありますので、おかしな点がありましたらどなたか補足修正して下さい。

arakororin
質問者

お礼

「メソッドエリア」クラスを保管する場所 「ヒープ」インスタンスを保管する場所 「Javaスタック」実行のためのエリア(スレッドごとに用意) 「フレーム」メソッド等がコールされる度に必要な情報を保管 なるほどー。この辺がわかっていないからごちゃごちゃだったんですね。非常によくわかりました。 >staticでないものはnewによりインスタンスを作成する必要があり、これは実はけっこうなCPUへの負荷になります。 なるほど。Springフレームワークでは、サーバー起動時にすべてのインスタンスを生成し、終了時まで保持するのですが、これっていい設計なのかなーと非常に疑問に思っていましたが、メモリを犠牲にする代わりに反応速度を向上させているのですね。 ところで、Kyon2_PaPaさんはどんな方法でこの辺のところを勉強されたんですかね。という疑問を持ちました。研修に行っても本で読んでもこの辺のところを詳しく書いてあるものってなかったように思ったので。 とにかく、この辺のことをちゃんと分かっておられる方ってすごいなぁと思います。私も精進して理解できるようになりたいと思いました。非常に丁寧なご回答ありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (2)

  • PecoPlus
  • ベストアンサー率76% (144/188)
回答No.3

 こんばんは。  この話はどこかで聞いたなと思い、探してみたらありました。  よその掲示板の参照で恐縮なんですが、読んでみてください。 http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=4833&forum=12  これによると、私たちがクラスのインスタンスを信じているものは、クラスのフィールド変数の集まりとその他もろもろのちょっとした情報で、Cで言う構造体に毛が生えたようなものでしかないようです。  つまり、 class Point {   int x;   int y;   public double distance() {     return Math.sqrt(x * x + y * y);   }   public void hogehoge() {     めちゃめちゃ長いコード。     ・・・   }   その他めちゃめちゃたくさんのメソッドたち。   ・・・ }  このPointクラスのインスタンスを生成すると、  まず、はじめてインスタンス化された場合、クラスがロードされます。  そして、ヒープメモリ上に、ちょっとした情報 + int x(4byte) + int y(4byte) これだけ書き込まれます。  メソッドはヒープには書き込まれず、ロードされたクラスのものが使われます。  二回目では、すでにクラスがロードされているので、クラスのロードは省略され、やはり同じようにフィールド変数の固まり、構造体もどきが作られます。  静的メソッドもインスタンスメソッドも、すでにあるものが使いまわされるようです。  ですので、インスタンスのサイズはフィールド変数の量に依存しているようです。  この手の誤解の原因は、オブジェクト指向にあると思います。  オブジェクト指向では、「データと関連する関数をくっつけてしまえ」という考え方で、うまいごまかしによって、プログラマーは、データと関数がくっついているように錯覚されられますが、結局はくっついてなんていないってことが混乱の元だと思います。  たとえば、上のPointクラスを使う場合は、 Point p = new Point(); p.x = 2; p.y = 2; double d = p.distance();  こんな感じになると思いますが、仮想マシンの内部では、 Point p = new Point();  ↑インスタンスpは実は構造体みたいなもので、変数x,yを保持するデータの塊でしかない p.x = 2; p.y = 2; double d = distance(p);  ↑インスタンスメソッドdistanceは実は構造体pを引数に持つ関数でしかない。  ↓インスタンスメソッドdistanceは、実はこんな感じになってる。 public double distance(Point this) {   return Math.sqrt(this.x * this.x + this.y * this.y); }  つまり、インスタンスメソッドは隠れ引数thisを持つ特殊な関数でしかない。  それに対し、staticなメソッドは何かというと、隠れ引数thisを持たない純粋な普通の関数ということです。  インスタンスは構造体でしかない。  メソッドは関数でしかない。  このことを思い出すと、 1.メソッドをヒープ領域に複数展開する必要はない。 2.スレッドとオブジェクトは直行する概念である。  ということがわかりやすくなると思います。  特に2では、C言語で複数のスレッドが一つの関数にアクセスしたからといって、その関数が複数あるとか思いませんよね。  マルチスレッドで、気をつけなければいけないのは、データの整合性であって、関数は関数でしかないってことです。  実際、細かいところは違うと思いますが、基本はこんな感じになっているのだと、私は理解しています。 「Java 謎+落とし穴」前橋和弥 著  この本読むと、目から鱗がペロッと取れますよ。

arakororin
質問者

お礼

>この手の誤解の原因は、オブジェクト指向にあると思います。 >オブジェクト指向では、「データと関連する関数をくっつけてしまえ」という考え方で、うまいごまかしによって、プログラマーは、データと関数がくっついているように錯覚されられますが、結局はくっついてなんていないってことが混乱の元だと思います。 なるほどなるほど。面白い話ですね。 >ヒープメモリ上に、ちょっとした情報 + int x(4byte) + int y(4byte) これだけ書き込まれます。 なんかむちゃくちゃテクニカルな話ですね。ちょっと私の今の理解を超えているのですが、参考になる話です。じっくり勉強したいと思います。 どうもありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • sire
  • ベストアンサー率62% (22/35)
回答No.2

なるほどー、いろいろ疑問をお持ちなんですね。 正確なことは知らないのですが、(おそらく概念的なことが聞きたいのだと思いますので) 1)staticメソッドを使用してしまうと、メモリ上に残ってしまうからあまり使うななどとよく言うと思います。 これに関してはケースbyケースだと思います。 2)staticでないメソッドは使用されている間はstaticと同じように管理され、使用後に開放対象となるということなのか。 イメージとしてはいいのだと思いますが、細かいことで言いますと、staticメソッドはクラスロード時にスタティック領域に、staticでないメソッドはヒープ領域に展開されるのだと思います。そして使用後に開放対象というよりは、ガーベッジコレクトの対象はヒープ領域の参照されていない"オブジェクト"なので、メソッド単位でメソッド使用後に開放、ではなくてその"クラス"が使用されなくなると、そのクラスのメンバメソッドが開放対象になる、ということだと思います。 3)staticメソッドであっても、同時に2箇所からアクセスされたら2つ分のメモリを消費するはずではないのか。 それはそうだと思います。その際のメモリ消費はスタック領域に取られると思います。 4)これは実質メモリ上に複数のメソッドが存在することにならないのか… ここで言う「メソッド」と「スレッド」を置き換えると正しいと思います。 5)ロードの際にはクラス単位で読み込まれるのか、メソッド単位なのか。 ”2)”と同様ですが、クラス単位になります。 メモリを意識するというのは大変良いことだと思います。特にCでは気をつけないといけませんが、Javaは気にしないのでこの辺りを考えないことが多くなっていると思います。メモリの領域の話、スレッドや同期の話この辺りを押さえておくと疑問点も解消すると思います。

arakororin
質問者

お礼

私もCからJavaに入ってのですが、ぜんぜんと言っていいほど分かっていなくて。周りの人でもちゃんと理解している人は少ないような気もするのですが。なんとなーく、というレベルの理解から、ダイブレベルアップしたと思います。(それでもまだまだですけど) どうもありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • staticのメリット、デメリット

    こんばんは。 staticについて、だんだんわからなくなってきました・・・。 例えばあるクラスの変数で、 static int i = 10; とあった場合、参照クラスからiの値を見ると、必ず10ですよね? static修飾子がついているメソッドの場合、挙動はどうなるのでしょう? staticなメソッドはインスタンス化しなくても呼び出せる。 つまり、メモリ上に1箇所しか存在しないので、同時にアクセスされた場合(synchronizedしていない場合)はどうなるのかがわかりません・・・。 メソッドには引数が存在し、returnがStringやintなどの場合です。 returnは保証(参照側にとって望む値と言う意味で)されるのでしょうか? インスタンス化をしない事によって、パフォーマンスが向上するのでしょうけれど、いまいちメリットがわかりません・・・。 public、privateにかかわらず、メソッドをstatic修飾するメリットを教えてください。 同時にデメリットもお願いします。 以上、下手くそな文章ですみません・・・。 (^^ゞ

    • ベストアンサー
    • Java
  • Javaのstaticメソッドの意味を教えて下さい

    Javaのstaticメソッドの意味を教えて下さい。 現在の解釈では、staticなメソッドとはインスタンスの生成に依存せず呼び出せる処理のことだと認識しています。逆にstaticでないメソッドは、生成後のインスタンスにしか使えない処理だと理解しています。 しかし、コンストラクタがstaticでないことに気がつき、疑問が生まれました。コンストラクタはstaticではないので、コンストラクタを呼び出す時にはそのクラスのインスタンスが必要になるはずです。しかしこのインスタンスを作るのにもコンストラクタが必要です。コンストラクタを使うためにインスタンスが必要で、インスタンスを作るためにコンストラクタが必要という無限ループになってしまいました。 そこで改めて、staticメソッドの意味を教えて頂きたいです。

  • 継承・実装の関係で悩んでいます。

    継承・実装の関係について悩んでいます。 ここでは、アクセス制御を考えずに、インスタンスかstaticかabstract(ここではabstract final staticやabstract classのこと)の違いで、どう継承するのか考えています。 // 継承 はメソッドのオーバーライドのことを考えます。(オーバーロードは考えない) クラスAからクラスBでオーバーライドしたメソッドは、 クラスCでさらにオーバーライドできるのでしょうか? クラスCが クラスBのクラスAからオーバーライドしたメソッド をクラスBのメソッドとして見たときに、オーバーライドすることは可能なのでしょうか? クラスA │ インスタンスフィールドA │ staticフィールドA │ │ クラスA() { } │ │ インスタンスメソッドA () { } │ staticメソッドB() { } ↓ クラスB extends クラスA │ インスタンスフィールドA // 継承 │ インスタンスフィールドB │ staticフィールドB │ │ サブクラス1() { } // コンストラクタは継承しない、super()で呼び出す │ │ インスタンスメソッドA () { } // 継承 │ │ インスタンスメソッドB () { } │ staticメソッドB () { } ↓ クラスC extends クラスB implements インタフェースD, ... ↑ インスタンスフィールドA // クラスBのフィールドを継承 │ インスタンスフィールドB // クラスBのフィールドを継承 │ インスタンスフィールドC │ staticフィールドC │ │ サブクラス2() { } │ │ インスタンスメソッドA () { } // クラスBのメソッドを継承 │ インスタンスメソッドB () { } // クラスBのメソッドを継承 │ インスタンスメソッドD () { } // インタフェースDのメソッドを実装 │ インスタンスメソッドE () { } // インタフェースDのメソッドを実装 │ │ │ インスタンスメソッドC () { } │ staticメソッドC() { } │ interface インタフェースD extends インタフェースE ↑ │ staticフィールドD // public static final │ │ インスタンスメソッドD() { } // public abstract │ インスタンスメソッドE() { } // 継承 │ interface インタフェースE staticフィールドE // public static final インスタンスメソッドE() { } // public abstract

    • ベストアンサー
    • Java
  • 「static宣言されているメンバ関数」は、「インスタンスメソッド」な

    「static宣言されているメンバ関数」は、「インスタンスメソッド」なのでしょうか? それとも、「クラスメソッド」なのでしょうか? 先日、下記内容で質問して、その時は分かったつもりだったのですが、 やっぱり分かってなかったようなので、教えてください。 ▽「クラス関数」「メンバ関数」「メソッド」の違いを教えてください。   http://okwave.jp/qa/q5858806.html 例) ▽前提 ・Aクラスのインスタンスa ・static宣言されたpublicメソッド「static_public_method」 ・static宣言されていないpublicメソッド「public_method」 ▽メンバ関数呼び出し ・$a->static_public_method() ・A::static_public_method() ・A::public_method() このとき、「$a->static_public_method()」は、インスタンス経由でアクセスすることになるので、 「インスタンスメソッド」になるのでしょうか。それとも、static(静的)宣言しているので、「クラスメソッド」になるのでしょうか? また、「A::static_public_method()」や、「A::public_method()」は、どちらになるのでしょうか? ※現在、頭の中がこんがらがっているのは、下記3点です。どれかひとつでも構わないので、分かりやすい考え方等あれば、ぜひ教えてください。 ・「static宣言したメンバ関数」は、「インスタンスメソッド」? 「クラスメソッド」? ・「スタティック」宣言してるのに、メンバ関数へ、「->(アロー演算子)」(オブジェクト経由)でアクセスできる理由 ・「A::static_public_method()」と「A::public_method()」の違い

    • ベストアンサー
    • PHP
  • staticメソッドにするかどうかの判断

    staticメソッドの使いどころについて質問させてください。 まず、メソッドの内部で、そのメソッドが定義されているクラスのイ ンスタンス変数を使用している場合は、そのメソッドはインスタンス に依存するので、インスタンスメソッドにしかできないのはわかり ます。疑問なのは、「じゃあこのメソッド内でインスタンス変数を使 わないで引数で渡せるように作れば、インスタンスに依存しないの でstaticにしていいの?」(※)ということです。 さらにこうも思います。 なんとなくですが、※のようなことをしたらオブジェクト指向の意味 がない気がします。理由ははっきりわかりません。ほんとになんとなく です。(奇跡的にあたっていたとしても、理由を教えて下さいm(_ _)m) そして、さらに混乱するのが、ユーティリティの関数などでstaticメ ソッドになっているのを見ました。この場合はどうしてメソッドの引 数を指定するようにしていて、クラスのインスタンス変数を使うこと はしないのか。 以上、大変下手な文章ですが、混乱しているポイントが伝わったでし ょうか? よろしくお願いします。

  • doGetの引数に「入るモノ」について

    サーブレットにおけるクラスのdoGetメソッドについて 質問です(Tomcat3.2.1 + JDK1.3)。 public void doGet(HttpServletRequest request, HttpServletResponse response) 理屈で考えると、このdoGetメソッドの2つの仮引数 (request, response)には、 「インターフェースHttpServletRequestをimplements したクラス」のインスタンス ‥‥と、 「インターフェースHttpServletRequestをimplements したクラス」のインスタンス の2つのインスタンスが入れられるハズですよね。 そこで疑問なのが、 1. それら2つのクラスの名前はなんというのでしょう? (というのはインターフェースHttpServletRequestや インターフェースHttpServletResponseを 実装したクラスのメソッドの処理内容の定義を 見たいのです‥) 2. 誰がそのクラスをインスタンス化しているのでしょう? (サーブレットコンテナのTomcat? JRE? JVM?‥ いったい誰が?‥‥用語の区別も曖昧なのですが) 補足すると、 Javaアプリケーションのmainメソッドでは、 public static void main(String[] args) として、コマンドラインの引数がそのまま仮引数argsに 入る。じゃあ、サーブレットの場合はどうなのか? といった感じです。 2.のほうはトンチンカンな質問かもしれませんが、 1.の質問は切実です。 ご存知の方、教えてください。

    • ベストアンサー
    • Java
  • Javaのサーブレットについて(マルチスレッド)

    サーブレットについて質問です。 サーブレットは1インスタンス・複数スレッドという形態で、 サーブレットコンテナにより管理されていると思います。 では、このコンテナ内では、どのように1インスタンス・複数スレッドを 実現しているのでしょうか? 以下、自分なりに考えたプログラムです。 たぶん、このようにすれば、1インスタンス・複数スレッドになるのかと思うのですが。 どなたかご教授願いますでしょうか。 よろしくお願いいたします。 【1インスタンス・複数スレッドプログラム例】 public class TestThread extends Thread { public void run() { System.out.println("TestThread.run()"); } public static void main(String[] args) { Thread tt = new TestThread(); Thread th1 = new Thread(tt); Thread th2 = new Thread(tt); th1.start(); th2.start(); } }

  • javaについてです。

    このプログラミング全部を教えて下さい。 staticでないものでお願いします。 ●CustomerManagerクラスに以下のフィールドを作成する  ・他のクラスからアクセスできない長さ5のCustomerCardの配列  ・整数型フィールド(名称:index、初期値0) ●CustomerManagerクラスに以下のメソッドを作成する   ・メソッド1  メソッド名:addCustomerCard  引数1:整数型  引数2:文字列型  動作:    ・配列のindex番目にCustomerCardインスタンスを作成    ・作ったインスタンスに引数の2つを設定    ・indexを1増やす ・メソッド2  メソッド名:printAllInfo  引数なし  動作:   配列にいれられた全てのCustomerCardの情報を表示する ●mainメソッドを以下のように修正する  addCustomerCardメソッドを使って以下の情報を登録する     ID = 1 , 名前 = "山田一郎"   ID = 2 , 名前 = "鈴木太郎"   ID = 3 , 名前 = "田中次郎"  printAllInfoメソッドを使って登録された全ての情報を表示する 以上よろしくお願い致します。

    • ベストアンサー
    • Java
  • Java 静的メソッドとインスタンスメソッド

    静的メソッドとインスタンスメソッドの使い分けがよくわかりません。 私の認識は 静的メソッド:インスタンスメンバ変数を参照する必要がない処理 インスタンスメソッド:インスタンスメンバ変数を参照して行う処理 と思っています。 よって、例えばDAOを作成する場合、select、update、insertを実行するメソッドを作成しますが、これらはクラスのインスタンスメンバ変数を参照する必要がないので静的メソッドにするべきだと思っています。 しかし、本やネットのDAOのサンプルプログラムはインスタンスメソッドで作成されています。 これらは、private static String の変数(SQL文が記述されている)を参照していますが、インスタンスメンバ変数は参照していません。 なぜselect、update、insertのメソッドをインスタンスメソッドにする必要があるのかわかりません。 静的メソッドとインスタンスメソッドの使い分けの基準を教えていただけないでしょうか。 よろしくお願いします。

    • ベストアンサー
    • Java
  • Javaサーブレットのライフサイクルについて

    Javaサーブレットのライフサイクルについての質問です。 まだJavaサーブレットの開発に携わってから間もないので、不足な点がありましたら申し訳ございません。 サーブレットは一度アクセスするとサーブレットコンテナ上で動作を続けますが(コンテナ上で実体が常駐する)、サーブレットがデータベースと接続しているという前提での文ですが、サーブレットの中のdoPostメソッド内でtry~catch~finally文のfinallyのところでデータベースの切断(close()メソッドで)等のリソースの開放を行い、それ以降はそのサーブレットにアクセスが無くなったとしたら、サーブレットはどの位の期間でライフサイクルが終了するのでしょうか。 サーブレットではdestroyメソッドでサーブレットの消滅がされると思いますが、サーブレットへのアクセスがそれ以降無かったりしても、もう一度アクセスされたりしても、ライフサイクルを考えるとdestroyを入れた方がよいのか、という事もどうすればよいのか分からなくて・・・。 是非、サーブレットのライフサイクルに関して詳しい方がいらっしゃいましたら、ご教授宜しくお願いします。

    • ベストアンサー
    • Java