なんか基本的なことなんですけど、ちゃんと把握していないなーと思って質問します。
staticメソッドを使用してしまうと、メモリ上に残ってしまうからあまり使うななどとよく言うと思います。これに対して、staticでないメソッドは使用されている間はstaticと同じように管理され、使用後に開放対象となるということなのか。というか、非staticメソッドは、2箇所から同時にアクセスされたらメソッド2つ分のメモリを使用するのか。その前に、staticメソッドであっても、同時に2箇所からアクセスされたら2つ分のメモリを消費するはずではないのか。
サーブレットでは、アクセスのあったサーブレットクラスがインスタンス化され、そのインスタンスが何度も使い回される、との記述を見たが、複数のアクセスがあったときに同時に処理ができるが、これは実質メモリ上に複数のメソッドが存在することにならないのか…
ロードの際にはクラス単位で読み込まれるのか、メソッド単位なのか。staticメソッドと非staticメソッドが混在しているクラスでは、読込み、開放はメソッド単位で行っているのか。
などの疑問を持っています。以上、まとまりのない疑問ですが、なんとなく私がわからない部分はわかっていただいたのではないかと思います。この辺の話について、よろしくお願いします。
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メソッドはクラス内に保持されるため、同様にスレッドが終了するまでメモリ内に置かれます。
なお、私もまだまだ勉強不足なところがありますので、おかしな点がありましたらどなたか補足修正して下さい。
「メソッドエリア」クラスを保管する場所
「ヒープ」インスタンスを保管する場所
「Javaスタック」実行のためのエリア(スレッドごとに用意)
「フレーム」メソッド等がコールされる度に必要な情報を保管
なるほどー。この辺がわかっていないからごちゃごちゃだったんですね。非常によくわかりました。
>staticでないものはnewによりインスタンスを作成する必要があり、これは実はけっこうなCPUへの負荷になります。
なるほど。Springフレームワークでは、サーバー起動時にすべてのインスタンスを生成し、終了時まで保持するのですが、これっていい設計なのかなーと非常に疑問に思っていましたが、メモリを犠牲にする代わりに反応速度を向上させているのですね。
ところで、Kyon2_PaPaさんはどんな方法でこの辺のところを勉強されたんですかね。という疑問を持ちました。研修に行っても本で読んでもこの辺のところを詳しく書いてあるものってなかったように思ったので。
とにかく、この辺のことをちゃんと分かっておられる方ってすごいなぁと思います。私も精進して理解できるようになりたいと思いました。非常に丁寧なご回答ありがとうございました。
No.3
- 回答日時:
こんばんは。
この話はどこかで聞いたなと思い、探してみたらありました。
よその掲示板の参照で恐縮なんですが、読んでみてください。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.ph …
これによると、私たちがクラスのインスタンスを信じているものは、クラスのフィールド変数の集まりとその他もろもろのちょっとした情報で、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 謎+落とし穴」前橋和弥 著
この本読むと、目から鱗がペロッと取れますよ。
>この手の誤解の原因は、オブジェクト指向にあると思います。
>オブジェクト指向では、「データと関連する関数をくっつけてしまえ」という考え方で、うまいごまかしによって、プログラマーは、データと関数がくっついているように錯覚されられますが、結局はくっついてなんていないってことが混乱の元だと思います。
なるほどなるほど。面白い話ですね。
>ヒープメモリ上に、ちょっとした情報 + int x(4byte) + int y(4byte) これだけ書き込まれます。
なんかむちゃくちゃテクニカルな話ですね。ちょっと私の今の理解を超えているのですが、参考になる話です。じっくり勉強したいと思います。
どうもありがとうございました。
No.2
- 回答日時:
なるほどー、いろいろ疑問をお持ちなんですね。
正確なことは知らないのですが、(おそらく概念的なことが聞きたいのだと思いますので)
1)staticメソッドを使用してしまうと、メモリ上に残ってしまうからあまり使うななどとよく言うと思います。
これに関してはケースbyケースだと思います。
2)staticでないメソッドは使用されている間はstaticと同じように管理され、使用後に開放対象となるということなのか。
イメージとしてはいいのだと思いますが、細かいことで言いますと、staticメソッドはクラスロード時にスタティック領域に、staticでないメソッドはヒープ領域に展開されるのだと思います。そして使用後に開放対象というよりは、ガーベッジコレクトの対象はヒープ領域の参照されていない"オブジェクト"なので、メソッド単位でメソッド使用後に開放、ではなくてその"クラス"が使用されなくなると、そのクラスのメンバメソッドが開放対象になる、ということだと思います。
3)staticメソッドであっても、同時に2箇所からアクセスされたら2つ分のメモリを消費するはずではないのか。
それはそうだと思います。その際のメモリ消費はスタック領域に取られると思います。
4)これは実質メモリ上に複数のメソッドが存在することにならないのか…
ここで言う「メソッド」と「スレッド」を置き換えると正しいと思います。
5)ロードの際にはクラス単位で読み込まれるのか、メソッド単位なのか。
”2)”と同様ですが、クラス単位になります。
メモリを意識するというのは大変良いことだと思います。特にCでは気をつけないといけませんが、Javaは気にしないのでこの辺りを考えないことが多くなっていると思います。メモリの領域の話、スレッドや同期の話この辺りを押さえておくと疑問点も解消すると思います。
私もCからJavaに入ってのですが、ぜんぜんと言っていいほど分かっていなくて。周りの人でもちゃんと理解している人は少ないような気もするのですが。なんとなーく、というレベルの理解から、ダイブレベルアップしたと思います。(それでもまだまだですけど)
どうもありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・【大喜利】【投稿~11/12】 急に朝起こしてきた母親に言われた一言とは?
- ・好きな和訳タイトルを教えてください
- ・うちのカレーにはこれが入ってる!って食材ありますか?
- ・好きな「お肉」は?
- ・あなたは何にトキメキますか?
- ・おすすめのモーニング・朝食メニューを教えて!
- ・「覚え間違い」を教えてください!
- ・とっておきの手土産を教えて
- ・「平成」を感じるもの
- ・秘密基地、どこに作った?
- ・【お題】NEW演歌
- ・カンパ〜イ!←最初の1杯目、なに頼む?
- ・一回も披露したことのない豆知識
- ・これ何て呼びますか
- ・チョコミントアイス
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・これ何て呼びますか Part2
- ・許せない心理テスト
- ・この人頭いいなと思ったエピソード
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・あなたの習慣について教えてください!!
- ・ハマっている「お菓子」を教えて!
- ・高校三年生の合唱祭で何を歌いましたか?
- ・【大喜利】【投稿~11/1】 存在しそうで存在しないモノマネ芸人の名前を教えてください
- ・好きなおでんの具材ドラフト会議しましょう
- ・餃子を食べるとき、何をつけますか?
- ・あなたの「必」の書き順を教えてください
- ・ギリギリ行けるお一人様のライン
- ・10代と話して驚いたこと
- ・家の中でのこだわりスペースはどこですか?
- ・つい集めてしまうものはなんですか?
- ・自分のセンスや笑いの好みに影響を受けた作品を教えて
- ・【お題】引っかけ問題(締め切り10月27日(日)23時)
- ・大人になっても苦手な食べ物、ありますか?
- ・14歳の自分に衝撃の事実を告げてください
- ・架空の映画のネタバレレビュー
- ・「お昼の放送」の思い出
- ・昨日見た夢を教えて下さい
- ・ちょっと先の未来クイズ第4問
- ・【大喜利】【投稿~10/21(月)】買ったばかりの自転車を分解してひと言
- ・メモのコツを教えてください!
- ・CDの保有枚数を教えてください
- ・ホテルを選ぶとき、これだけは譲れない条件TOP3は?
- ・家・車以外で、人生で一番奮発した買い物
- ・人生最悪の忘れ物
- ・【コナン30周年】嘘でしょ!?と思った○○周年を教えて【ハルヒ20周年】
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C# 点の描き方をおしえてくだ...
-
レコード件数の表示
-
メソッドの引数に指定されてい...
-
public static void main (Stri...
-
Java初級 引数に適用できません
-
String.containsの反対機能はあ...
-
[クラス名].[メソッド名].[メソ...
-
別クラスのmainメソッドの実行
-
Javaの文法
-
コマンドライン引数のチェック
-
YYYYMMDD書式の日付に対する適...
-
Strutsでチェックボックスの値...
-
説明文書でのメソッド・変数の...
-
Google Apps Script で getRang...
-
変数名の付け方
-
「タイプ初期化子が例外をスロ...
-
エクセルVBAで、条件に一致する...
-
ServletContextLisntener#conte...
-
Excel VBA 定数にオブジェクト...
-
C#において、同じインスタンス...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
String.containsの反対機能はあ...
-
Java初級 引数に適用できません
-
StringBufferからStringへキャ...
-
レコード件数の表示
-
なぜprotected overrideなのか
-
Java初心者です、エラーの意味...
-
C# でメソッドに送られてきたOb...
-
Google Apps Script で getRang...
-
abstract と static を一緒に付...
-
C# 点の描き方をおしえてくだ...
-
別クラスのmainメソッドの実行
-
javaに"search"という関数 or ...
-
YYYYMMDD書式の日付に対する適...
-
Strutsでチェックボックスの値...
-
Javaの関数名が長い?
-
return new使用時
-
readLine()ではじめから読み直...
-
シェルスクリプトからのJavaメ...
-
java ジェネリックスに関して
-
Jakarta POI での、excelのシ...
おすすめ情報