• ベストアンサー

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

専門家に質問してみよう