88

スタックとヒープの間のメモリ割り当ての基本と混同しています。標準の定義(誰もが言うこと)に従って、すべての値型はスタックに割り当てられ、参照型はヒープに入れられます。

次の例を考えてみましょう。

class MyClass
{
    int myInt = 0;    
    string myString = "Something";
}

class Program
{
    static void Main(string[] args)
    {
       MyClass m = new MyClass();
    }
}

さて、メモリ割り当てはc#でどのように行われますか?MyClass(つまり)のオブジェクトはm完全にヒープに割り当てられますか?つまり、int myInt両方string myStringともヒープになりますか?

または、オブジェクトは2つの部分に分割され、スタックとヒープの両方のメモリ位置に割り当てられますか?

4

9 に答える 9

71

実装の詳細として、オブジェクトがどこに割り当てられるかという問題を検討する必要があります。オブジェクトのビットがどこに格納されているかは、正確には関係ありません。オブジェクトが参照型であるか値型であるかは重要ですが、ガベージコレクションの動作を最適化する必要が生じるまで、オブジェクトがどこに格納されるかを心配する必要はありません。

現在の実装では、参照型は常にヒープに割り当てられますが、値型はスタックに割り当てられる場合がありますが、必ずしもそうとは限りません値型は、参照型に含まれておらず、レジスタに割り当てられていない、ボックス化されていないエスケープされていないローカル変数または一時変数である場合にのみ、スタックに割り当てられます。

  • 値型がクラスの一部である場合(あなたの例のように)、それは最終的にヒープになります。
  • ボックス化されている場合は、ヒープ上に配置されます。
  • 配列内にある場合は、ヒープ上に配置されます。
  • 静的変数の場合は、ヒープ上に配置されます。
  • クロージャーによってキャプチャされた場合、ヒープ上に配置されます。
  • イテレータまたは非同期ブロックで使用されている場合は、ヒープ上に配置されます。
  • 安全でないコードまたは管理されていないコードによって作成された場合は、任意のタイプのデータ構造(必ずしもスタックまたはヒープである必要はありません)に割り当てることができます。

見逃したことはありますか?

もちろん、このトピックに関するEric Lippertの投稿にリンクしていなかったら、私は失望するでしょう。

于 2010-12-20T06:09:11.013 に答える
58

mはヒープに割り当てられ、これには。が含まれますmyInt。プリミティブ型(および構造体)がスタックに割り当てられる状況は、メソッドの呼び出し中に発生します。メソッドの呼び出しでは、スタック上のローカル変数用のスペースが割り当てられます(高速であるため)。例えば:

class MyClass
{
    int myInt = 0;

    string myString = "Something";

    void Foo(int x, int y) {
       int rv = x + y + myInt;
       myInt = 2^rv;
    }
}

rv、、xyすべてスタックにあります。ヒープのどこかにあります(そしてポインタmyIntを介してアクセスする必要があります)。this

于 2010-12-20T06:08:55.523 に答える
24

「すべてのVALUEタイプがスタックに割り当てられる」というのは非常に間違っています。構造体変数は、メソッド変数としてスタック上に存在できます。ただし、タイプのフィールドはそのタイプと同じです。フィールドの宣言型がクラスの場合、値はそのオブジェクトの一部としてヒープ上にあります。フィールドの宣言型が構造体である場合、その構造体が存在する場所であればどこでも、フィールドはその構造体の一部です。

メソッド変数でさえ、キャプチャされている場合(ラムダ/アノンメソッド)、または(たとえば)イテレータブロックの一部である場合は、ヒープ上に存在する可能性があります。

于 2010-12-20T06:09:22.877 に答える
2

スタック

は、およびstackを格納するためのメモリのブロックです。関数が開始および終了すると、スタックは論理的に拡大および縮小します。local variablesparameters

次の方法を検討してください。

public static int Factorial (int x)
{
    if (x == 0) 
    {
        return 1;
    }

    return x * Factorial (x - 1);
}

このメソッドは再帰的です。つまり、それ自体を呼び出します。メソッドに入るたびに、新しいintがスタックに割り当てられ、メソッドが終了するたびに、intの割り当てが解除されます


ヒープ

  • ヒープは、objects(つまりreference-type instances)が存在するメモリのブロックです。新しいオブジェクトが作成されるたびに、そのオブジェクトはヒープに割り当てられ、そのオブジェクトへの参照が返されます。プログラムの実行中、新しいオブジェクトが作成されると、ヒープがいっぱいになり始めます。ランタイムには、ヒープからオブジェクトの割り当てを定期的に解除するガベージコレクターがあるため、プログラムは実行されませんOut Of Memory。オブジェクトは、それ自体が参照されていない場合はすぐに割り当て解除の対象になりますalive
  • ヒープも格納しstatic fieldsます。ヒープに割り当てられたオブジェクト(ガベージコレクションが発生する可能性がある)とは異なり、these live until the application domain is torn down

次の方法を検討してください。

using System;
using System.Text;

class Test
{
    public static void Main()
    {
        StringBuilder ref1 = new StringBuilder ("object1");
        Console.WriteLine (ref1);
        // The StringBuilder referenced by ref1 is now eligible for GC.

        StringBuilder ref2 = new StringBuilder ("object2");
        StringBuilder ref3 = ref2;
        // The StringBuilder referenced by ref2 is NOT yet eligible for GC.
        Console.WriteLine (ref3); // object2
    }
}    

上記の例では、変数ref1によって参照されるStringBuilderオブジェクトを作成することから始め、次にその内容を書き出します。そのStringBuilderオブジェクトは、その後何も使用しないため、すぐにガベージコレクションの対象になります。次に、変数ref2によって参照される別のStringBuilderを作成し、その参照をref3にコピーします。それ以降はref2は使用されませんが、ref3は同じStringBuilderオブジェクトを存続させ、ref3の使用が終了するまでコレクションの対象にならないようにします。

値型インスタンス(およびオブジェクト参照)は、変数が宣言されている場所ならどこにでも存在します。インスタンスがクラスタイプ内のフィールドとして、または配列要素として宣言されている場合、そのインスタンスはヒープ上に存在します。

于 2018-01-28T18:22:26.180 に答える
1

簡単な対策

値型はスタックに適用できます。これは、いくつかの未来主義者のデータ構造に割り当てることができる実装上の詳細です。

したがって、値と参照型がどのように機能するかを理解することをお勧めします。値型は値によってコピーされます。つまり、値型をパラメータとしてFUNCTIONに渡すと、本来コピーされるよりも、まったく新しいコピーが作成されます。 。

参照型は参照によって渡されます(参照が将来のバージョンでアドレスを再度格納することを考慮しないでください。他のデータ構造に格納される可能性があります)。

だからあなたの場合

myIntは、参照型をオフコースするクラスにカプセル化されるintであるため、「THEHEAP」に格納されるクラスのインスタンスに関連付けられます。

私は提案します、あなたはERICLIPPERTSによって書かれたブログを読み始めることができます。

エリックのブログ

于 2010-12-20T07:07:08.497 に答える
1

オブジェクトが作成されるたびに、ヒープと呼ばれるメモリの領域に入ります。intやdoubleなどのプリミティブ変数は、ローカルメソッド変数の場合はスタックに割り当てられ、メンバー変数の場合はヒープに割り当てられます。メソッドでは、メソッドが呼び出されるとローカル変数がスタックにプッシュされ、メソッド呼び出しが完了するとスタックポインタがデクリメントされます。マルチスレッドアプリケーションでは、各スレッドに独自のスタックがありますが、同じヒープを共有します。これが、ヒープスペースでの同時アクセスの問題を回避するためにコードに注意を払う必要がある理由です。スタックはスレッドセーフです(各スレッドには独自のスタックがあります)が、コードによる同期で保護されていない限り、ヒープはスレッドセーフではありません。

このリンクも役立ちますhttp://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/

于 2015-04-06T18:34:49.140 に答える
0

mはMyClassのオブジェクトへの参照であるため、mはメインスレッドのスタックに格納されますが、MyClassのオブジェクトはヒープに格納されます。したがって、myIntとmyStringはヒープに格納されます。mは単なる参照(メモリへのアドレス)であり、メインスタック上にあることに注意してください。mが割り当て解除されたら、GCはヒープからMyClassオブジェクトをクリアします。詳細については、この記事の4つの部分すべてをお読み くださいhttps://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net-パート-i/

于 2018-03-30T12:50:55.000 に答える
-1

標準の定義(誰もが言うこと)に従って、すべての値型はスタックに割り当てられ、参照型はヒープに入れられます。

これは間違っています。ローカル(関数のコンテキストで)値型/値型の配列のみがスタックに割り当てられます。他のすべてはヒープに割り当てられます。

于 2021-06-08T18:41:33.297 に答える