私が理解しているように、Javaでは、スタックメモリはプリミティブとメソッドの呼び出しを保持し、ヒープメモリはオブジェクトの格納に使用されます。
クラスがあるとします
class A {
int a ;
String b;
//getters and setters
}
クラスa
のプリミティブA
はどこに保存されますか?
なぜヒープメモリが存在するのですか?スタックにすべてを格納できないのはなぜですか?
オブジェクトがガベージコレクションされると、オブジェクトに関連付けられたスタックは破棄されますか?
スタックとヒープの基本的な違いは、値のライフサイクルです。
スタック値は、それらが作成された関数のスコープ内にのみ存在します。スタック値が返されると、それらは破棄されます。
ただし、ヒープ値はヒープ上に存在します。それらは、ある時点で作成され、別の時点で破棄されます(言語/ランタイムに応じて、GCまたは手動で)。
Javaは、スタックにプリミティブのみを保存します。これにより、スタックが小さく保たれ、個々のスタックフレームが小さく保たれるため、より多くのネストされた呼び出しが可能になります。
オブジェクトはヒープ上に作成され、参照(つまりプリミティブ)のみがスタックで渡されます。
したがって、オブジェクトを作成すると、オブジェクトに属するすべての変数とともにオブジェクトがヒープに置かれるため、関数呼び出しが戻った後もオブジェクトを保持できます。
プリミティブフィールドは、インスタンス化されたオブジェクトの一部として保存されますどこか。これがどこにあるかを考える最も簡単な方法は、ヒープです。 ただし、これは常に当てはまるわけではありません。 Javaの理論と実践:都市のパフォーマンスの伝説、 で説明されているように:
JVMは、エスケープ分析と呼ばれる手法を使用できます。これにより、特定のオブジェクトが存続期間全体にわたって単一のスレッドに限定されたままであり、その存続期間は特定のスタックフレームの存続期間によって制限されます。このようなオブジェクトは、ヒープではなくスタックに安全に割り当てることができます。さらに良いことに、小さなオブジェクトの場合、JVMは割り当てを完全に最適化し、オブジェクトのフィールドをレジスターに引き上げることができます。
したがって、「オブジェクトが作成され、フィールドもそこにある」と言うだけでなく、何かがヒープ上にあるのかスタック上にあるのかはわかりません。小さくて寿命の短いオブジェクトの場合、「オブジェクト」がメモリに存在せず、そのフィールドがレジスタに直接配置される可能性があることに注意してください。
論文は次のように締めくくられます。
JVMは驚くべきことに、開発者だけが知っていると想定していたものを理解するのに優れています。 JVMにケースバイケースでスタック割り当てとヒープ割り当てのどちらかを選択させることで、プログラマーにスタックに割り当てるかヒープに割り当てるかを悩ませることなく、スタック割り当てのパフォーマンス上の利点を得ることができます。
したがって、次のようなコードがあるとします。
_void foo(int arg) {
Bar qux = new Bar(arg);
...
}
_
_...
_はqux
がそのスコープを離れることを許可しない場合、qux
mayが割り当てられます代わりにスタック。これは実際にはVMの勝利です。これは、ガベージコレクションを行う必要がないことを意味します。スコープを離れると消えます。
ウィキペディアの エスケープ分析 の詳細。論文を詳しく知りたい方は、IBMの Escape Analysis for Java をご利用ください。 C#の世界から来る人には、 The Stack Is an Implementation Detail と The Truth About Value Types by Eric Lippertが見つかります適切な読み取り(Java型にも役立ちます。概念と側面の多くが同じまたは類似しているためです)。 なぜ.Netの本はスタックとヒープのメモリ割り当てについて話しているのですか? もこれについて説明しています。
では、なぜスタックやヒープがまったく必要なのでしょうか。スコープから外れるものの場合、スタックは高価になる可能性があります。コードを考えてみましょう:
_void foo(String arg) {
bar(arg);
...
}
void bar(String arg) {
qux(arg);
...
}
void qux(String arg) {
...
}
_
パラメータもスタックの一部です。ヒープがない場合は、スタックに値の完全なセットを渡します。これは、_"foo"
_および小さな文字列では問題ありませんが、誰かがその文字列に巨大なXMLファイルを挿入するとどうなりますか。各呼び出しは、巨大な文字列全体をスタックにコピーします-thatは非常に無駄です。
代わりに、直接スコープの外にあるライフ(別のスコープに渡された、他の誰かが維持している構造でスタックされた、など)を持つオブジェクトを、ヒープと呼ばれる別の領域に配置することをお勧めします。
スタックは必要ありませんスタック。仮に、スタックを使用しない言語(任意の深さ)を書くことができます。私が青年期に学んだ古いBASICはそうしました、gosub
呼び出しの8レベルしか実行できず、すべての変数はグローバルでした-スタックがありませんでした。
スタックの利点は、スコープとともに存在する変数がある場合、そのスコープを離れると、そのスタックフレームがポップされることです。存在するものと存在しないものを単純化します。プログラムは別のプロシージャ、新しいスタックフレームに移動します。プログラムがプロシージャに戻り、現在のスコープが表示されているプロシージャに戻ります。プログラムはプロシージャを離れ、スタック上のすべてのアイテムが割り当て解除されます。
これにより、スタックとヒープを使用するコードのランタイムを作成する人が本当に楽になります。それらは単にコードに取り組む多くの概念と方法であり、言語でコードを書く人がそれらを明示的に考えることから解放されることを可能にします。
スタックの性質は、断片化できないことも意味します。 メモリの断片化 はヒープの実際の問題です。いくつかのオブジェクトを割り当ててから、真ん中のオブジェクトをガベージコレクションしてから、次に大きいオブジェクトを割り当てるためのスペースを見つけます。その混乱。代わりにスタックに物を置くことができるということは、それに対処する必要がないことを意味します。
何かがガベージコレクションされると、それはなくなります。しかし、それはすでに忘れられているため、ガベージコレクションのみです。プログラムの現在の状態からアクセスできるプログラム内のオブジェクトへの参照はありません。
これはガベージコレクションの非常に大きな単純化であることを指摘しておきます。多くのガベージコレクタがあります(Java内でも)-さまざまなフラグ( docs を使用してガベージコレクタを微調整できます)。これらの動作は異なり、ニュアンスも異なります。それぞれがこの答えには深すぎるので、 Java Garbage Collection Basics を読んで、それがどのように機能するかをよりよく理解することができます。
とは言っても、スタックに何かが割り当てられた場合、System.gc()
の一部としてガベージコレクションは行われません。スタックフレームがポップすると、割り当てが解除されます。ヒープ上に何かがあり、スタック上の何かから参照されている場合、その時点ではガベージコレクションされません。
ほとんどの場合、その伝統。書かれたテキストブックとコンパイラクラス、およびさまざまなビットのドキュメントは、ヒープとスタックについて重要な役割を果たします。
しかし、今日の仮想マシン(JVMなど)は、これをプログラマーから隠そうとするために多大な時間を費やしています。どちらかが不足していて、理由を知る必要がある場合を除いて(ストレージ領域を適切に増やすだけでなく)、それも重要ではありませんたくさん。
オブジェクトはsomewhereであり、存在する適切な時間の間、正しく迅速にアクセスできる場所にあります。スタック上またはヒープ上にある場合-それは本当に問題ではありません。
Java=でない限りヒープ上でエスケープ分析を介してこれがセマンティクスに影響しないことを証明した後、最適化としてクラスインスタンスをスタックに割り当てます。ただし、これは実装の詳細であるため、マイクロ最適化の答えは「ヒープ上」です。
スタックメモリは、先入れ先出しで最後に割り当ておよび割り当て解除する必要があります。ヒープメモリは、任意の順序で割り当ておよび割り当て解除できます。
オブジェクトがガベージコレクションされると、スタックからそのオブジェクトを指す参照はなくなります。あったとしても、オブジェクトを存続させます。スタックプリミティブは、関数が戻るときに自動的に破棄されるため、ガベージコレクションはまったく行われません。
スタックメモリは、ローカル変数と関数呼び出しを格納するために使用されます。
ヒープメモリは、Javaでオブジェクトを格納するために使用されます。とにかく、オブジェクトはコードのどこに作成されます。
class A
のプリミティブa
はどこに保存されますか?
この場合、プリミティブaはクラスAオブジェクトに関連付けられています。したがって、ヒープメモリに作成されます。
なぜヒープメモリが存在するのですか?スタックにすべてを格納できないのはなぜですか?
オブジェクトがガベージコレクションされると、オブジェクトに関連付けられたスタックは破棄されますか?
ガベージコレクターはヒープメモリのスコープの下で機能するため、ルートからルートへの参照チェーンを持たないオブジェクトを破棄します。