web-dev-qa-db-ja.com

すべての静的メンバーはどこに保存されますか?

C#がメモリを管理する方法を学ぼうとしています。私は静的な要素にこだわっており、このテーマに関する多くのブログや記事を読んでいますが、満足のいく答えは見つかりません。

答えを見つけるのに役立つコードブロックを定義しましょう。

class myClass
{
    static string myStr = "String Data";
    static int myInt = 12;
}

皆さんがあなたの答えを共有する前に、この主題について私が知っている私の発見を共有させてください。自由に同意または反対し、正しい答えを見つけるのを手伝ってください。

  • 静的なのは一生です。
  • 静的な参照型(myStr)は、一生ヒープになります。
  • 静的な値型(myInt)は、一生スタックになります。

私を混乱させているのは、このテーマに関して、インターネットで見つけたいくつかの答えです。

混乱番号1:

プログラムが起動すると、関連するすべてのアセンブリがAppDomainに読み込まれます。アセンブリが読み込まれると、静的フィールドを含むすべての静的コンストラクターが呼び出されます。それらはそこに存在し、それらをアンロードする唯一の方法は、AppDomainをアンロードすることです。

上記の行では、すべての静的要素がAppDomainに格納されていることが明示的に言及されています。それでは、なぜインターネット上の誰もが「静的」要素はヒープ/スタックに保存されると言うのでしょうか?

混乱番号2:

すべての静的変数は、参照型または値型のどちらで宣言されているかに関係なく、ヒープに格納されます。

すべての静的変数がヒープに格納されている場合。次に、なぜ値型の静的変数はスタックに格納されると言う人がいますか?

C#の静的変数のメモリ管理を理解するために、ドットをつなげてください。貴重な時間をありがとうございました:)

39
Ali Asad

まず、これはすべて実装の詳細であることに注意してください。ランタイムが保証する唯一のものは:

  • 静的フィールドを要求すると、そこにあります
  • 型を使用する前のある時点で静的コンストラクターが実行されます

それはほとんどそれです。それ以外はすべて実装の詳細です-仕様はスタック、ヒープ、またはその他のことを気にしません。ランタイムの実装次第であり、有効なランタイムは、必要に応じてすべてをスタックまたはヒープに置くことができます。また、レジスタを忘れないでください。

さて、あなたがすでに拾い上げた誤解のいくつかを見てみましょう:

  • 静的は一生のものです-はい。それはいつ、どこに保存されているかについては何も言いません-あなたがそれを求めたときに利用できるというだけです。準拠ランタイムは、必要なメモリを自由に使用でき、フィールドをメモリにロードすることさえありません(たとえば、とにかく既にメモリにあるイメージに保持するなど)
  • 静的な場合、一生ヒープになります-ほとんどの場合、はい。しかし、それは仕様の一部ではなく、準拠するランタイムは、適切な保証が保持されている限り、必要な場所に格納することも、どこにも格納することもできません。また、「生涯」は「少なくともAppDomainの生涯」を意味することを忘れないでください。ドメインがアンロードされると、リリースされる場合とリリースされない場合があります。
  • 静的な値の型は一生スタックになります-ほとんどの場合、いいえ。繰り返しますが、実装の詳細ですが、スタックは静的な値に意味をなすものとは完全に異なるセマンティクスを持っています。そして、次のポイントはあなたにもっと多くの理由を与えます:
  • アセンブリがロードされると、静的フィールドを含むすべての静的コンストラクターが呼び出されます。-いいえ。そのような要件はなく、そのような保証もありません。あなたがこれに依存しているなら、あなたのプログラムは壊れるでしょう(そして、私は以前に何度も見ました)。繰り返しますが、実装の詳細ですが、現在のMSCLR実装では、静的変数は独自のヒープに割り当てられる傾向があり、静的変数が定義される型が必要になるまでに時間がかかります。静的コンストラクターで例外をスローすると、これを簡単に確認できます。おそらく、最初に型を参照するメソッドでTypeLoadExceptionが発生します(言うまでもなく、これは静的統計のデバッグを難しくします)。
  • 参照型はヒープに移動し、値型はスタックに移動します。-いいえ。これは、メカニズムとセマンティクスを混同しています。 2つの唯一の違いはセマンティクスです。他のすべては実装次第です。ランタイムがスタック上の参照型の参照セマンティクスを保持できる場合、それは完全に有効です。また、現在のMSCLRランタイムを使用しても、値の型は常に、たとえばボックス化されるたびに、または参照型のメンバーのように、ヒープに格納されます。

一部の人々は混乱する可能性があります。一部の人は、契約と実際の実装の違いを理解していません。単に何を話しているのか分からない人もいます。どちらが簡単かを知る簡単な方法があればいいのですが、そうではありません。疑わしい場合は、C#/ CLRの仕様にアクセスできますが、実際の現実ではなく、契約についてのみ説明しています。

管理メモリのポイントは、これらの実装の詳細を気にする必要がないことです。もちろん、あらゆる抽象化と同様にリークします。CPUのマイクロ命令、メモリキャッシングなど、さまざまなレイヤーや抽象化のすべてを通じて、物事が実際にどのようになっているのかを知ることは理にかなっています。しかし、それはrely onではありません-実装はいつでも変更でき、過去に何度もあります。

56
Luaan

プロセスがRAMに読み込まれるたびに、メモリは(プロセス内の)スタック、ヒープ、静的(.NETでは実際にはヒープ内でのみ知られるヒープ内の特別な領域)の3つの領域に大まかに分けられます。高周波ヒープ)。

静的な部分は、「静的な」メンバー変数とメソッドを保持します。静的とは正確には何ですか?クラスのインスタンスを作成する必要がないメソッドと変数は、静的であると定義されます

続きを読む こちら

6
Amir Popovich

作成されたクラスのインスタンスがあり、すべての静的メンバーが初期化されています。

通常、静的クラスのメンバーはヒープに格納され、値型のメンバーは通常スタックに格納されます。

これは必ずしもそうである必要はありません。詳細については this blogを読んでください。

これは、C#の言語デザイナーの1人であるEric Lippertによるものです。

ブログは、通常の知識に反して、値型がスタック上にあり、参照型がヒープ上にあることを確信していないことを示していますが、通常はそうです。

仕様で指定されていないだけです。

2
Mafii