クラスがあるとします
class Foo
{
public static bar;
}
あなたが言う時:
new Foo();
メモリには、このオブジェクト用にスペースが確保されていると想像できます。
...そしてあなたが再び言うとき:
new Foo();
...さて、オブジェクトに使用できる別のスペースがあります。
ただし、静的フィールドは正確にどこに存在しますか?
私が本当に学ぼうとしているのは:
オブジェクトへの参照は、参照するオブジェクトの同じフィールドをどのように参照しますか?
型システムの正確な詳細は実装に依存しますが、it dependおよびyouと言うだけでなく、詳細を説明します。気にしないでください。本 CLR via C#by Jeffrey Richter と記事-に従って、Microsoftの実装(.NET)でどのように機能するかを説明します。 CLRがランタイムオブジェクトを作成する方法を参照by Hanu Kommalapati et al。 ( 元のMSDN 2005年5月号 )。
クラスがあるとしましょう:
_class Foo
{
// Instance fields
string myBar = "Foobar";
int myNum;
// Static fields
static string bar = "Foobar";
static int num;
}
Foo myFoo = new Foo();
Type typeOfFoo = typeof(Foo);
_
インスタンスフィールドはどこにありますか?
new Foo()
と言うたびに、スペースがオブジェクトインスタンスに割り当てられて初期化され、コンストラクターが呼び出されます。このインスタンスは、以下の画像でFooのインスタンスとして示されています。インスタンスなどには、クラスのインスタンスフィールド(この場合はmyBar
およびmyNum
)のみが含まれ、ヒープに割り当てられたオブジェクトの場合は、ランタイムによって使用される2つの追加フィールド(_Sync block index
_および_Type handle
_)。タイプハンドルは、インスタンスのタイプを記述するType
オブジェクトへのポインターです。この場合、type of Fooです。
もう一度new Foo()
と言うと、新しいスペースが割り当てられ、そのタイプのインスタンスフィールド用のスペースが再び含まれます。ご覧のとおり、インスタンスフィールドはオブジェクトinstancesに関連付けられています。
ランタイムは、オブジェクトのデータの先頭からの固定オフセットに各インスタンスフィールドを配置します。たとえば、myBar
はオフセット+4にある可能性があります。インスタンスフィールドのアドレスは、単にオブジェクトのアドレスにフィールドのオフセットを加えたものです。
静的フィールドはどこにありますか?
C#およびJavaの静的フィールドは、オブジェクトインスタンスではなく、型に関連付けられています。クラス、構造体、および列挙型は型の例です。型ごとにType
オブジェクトも1つしかないため、型を記述するType
構造体の静的フィールドにスペースを割り当てることは理にかなっています。 C#とJavaが取ったアプローチ。
Type
オブジェクト1 タイプがランタイムによってロードされるときに作成されます。この構造には、ランタイムが新しいインスタンスを割り当てたり、メソッドを呼び出したり、キャストを実行したりするために必要なあらゆる種類の情報が含まれています。また、静的フィールド、この場合はbar
およびnum
のスペースも含まれます。
ランタイムは、各静的フィールドを型のデータの先頭からのオフセットに配置しました。これはタイプごとに異なります。たとえば、bar
はオフセット+64にある可能性があります。静的フィールドのアドレスは、Type
オブジェクトのアドレスにフィールドのオフセットを加えたものです。タイプは静的に知られています。
1)Microsoft .NETでは、MethodTableやEEClass構造など、複数の異なる構造がタイプを記述します。
これは、問題の実装に完全に依存します。 C#およびJavaの場合、ランタイムは変数のメモリを保存する場所を決定できます。 Cおよびほとんどのコンパイル済み言語では、コンパイラーがこの決定を行います。
そうは言っても、実際にはそれは問題ではないです。仕様によって使用方法が決定されるため、動作が保証されることを知っている変数を自由に使用できます。
Javaの場合、静的フィールドによって参照されるオブジェクトは、他のオブジェクトと同様に ヒープ上 に存在します。
ヒープは、すべてのクラスインスタンスおよび配列のメモリが割り当てられるランタイムデータ領域です。
クラスがロードされる の場合、フィールドは初期化されます(宣言に初期化が含まれる場合)。これは、次のいずれかが最初に発生する直前に発生します。
- クラスのインスタンスが作成されます。
- クラスによって宣言された静的メソッドが呼び出されます。
- クラスによって宣言された静的フィールドが割り当てられます。
- クラスによって宣言された静的フィールドが使用され、フィールドは定数変数ではありません(4.12.4)。
静的フィールドへのアクセスは、2つの特別なJVM命令 getstatic および putstatic を介して行われます。しかし、その区別を除けば、静的フィールドは非静的フィールドに似ています。
これは言語ごとに大きく異なり、プラットフォームごとに大きく異なることさえあります...
たとえば、.NET側では、静的メンバーは、管理するEEClass
定義に「関連付け」られており、canはヒープ-割り当てられた[〜#〜] or [〜#〜]「どこでも」割り当てられたメンバー(C#仕様はヒープ/スタックの動作を指定せず、VMの実装の詳細です)
例外がある場合もありますが、参照型の場合、new
- keywordは通常、「ヒープ」と呼ばれる内部データ構造にオブジェクトを作成します。ヒープは、CLR(共通言語ランタイム)によって管理されます。静的メンバー、インスタンスメンバー、ローカル変数のどれを使用しても違いはありません。
静的メンバーとインスタンスメンバー(キーワードstatic
のないもの)の違いは、静的メンバーはタイプ(クラス、構造体)ごとに1回だけ存在し、インスタンスメンバーはインスタンス(オブジェクトごと)ごとに1回存在することです。
静的または非静的なのは参照のみです。この区別は、参照されるオブジェクトには適用されません(オブジェクトが値型でない場合)。静的メンバー、インスタンスメンバー、およびローカル変数はすべて、同じオブジェクトを参照できます。
ImはC#にのみ精通しており、これは私の理解です:
その後、プログラムが起動し、関連するすべてのアセンブリがAppDomainに読み込まれます。アセンブリがロードされると、静的フィールドを含むすべての静的コンストラクタが呼び出されます。それらはそこに存在し、それらをアンロードする唯一の方法は、AppDomainをアンロードすることです。
静的メンバーと定数はヒープに格納されます。ガベージコレクションを取得できるヒープ上のオブジェクトとは異なり、静的メンバーと定数はAppdomainが破棄されるまで残ります。したがって、静的フィールドの処理には注意が必要です。
静的変数はオブジェクトではなくクラスに属しているため、bar
の数千のインスタントを初期化しても、メモリにはFoo
が1つしかありません。
仕様により、静的変数は Constant Pool に保存されます。 JVMはこの情報を永続世代に保存します。
これは、言語または言語のデザイナーによって異なります。 Javaについて話すと、静的メンバーはJVMのメソッド領域に格納され、すべてのオブジェクトがそれらにリンクされます。知っておくべきもう1つの重要なことは、クラスのオブジェクトを作成せずに静的データメンバーにアクセスできることです。つまり、静的データメンバーへのメモリの割り当ては、そのオブジェクトの作成に依存しません。クラス。
通常、静的変数はプログラムメモリのデータセグメントに保存されます。そのため、作成されている/実行中のプログラム内にあるすべてのクラスについて、データセグメントに静的変数が作成され、他のすべての変数はコードセグメントで初期化されます。
だから基本的には
+++++++++++++++++++++++++++++++++++++
+ DATA Segment
+ -static vars
+
+----------------------------------
+ instances | instances | instances|
+ | | |
ここでは、単一の領域がインスタンス間で共有されます。
from wikipedia "データ領域には、プログラムで使用されるグローバル変数と静的変数が含まれています。
明示的に値で初期化されました。」