Stack vs Heapの間のメモリ割り当ての基本と混乱しています。標準の定義(誰もが言うこと)に従って、すべてのValue TypesはStackおよびReferenceに割り当てられ、タイプは- ヒープ。
次の例を考えてみましょう。
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つの部分に分割され、両方のメモリ位置、つまりスタックとヒープに割り当てられますか?
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
、x
、y
はすべてスタック上にあります。 myInt
はヒープ上のどこかにあります(this
ポインターを介してアクセスする必要があります)。
実装の詳細としてwhereオブジェクトが割り当てられるという問題を考慮する必要があります。オブジェクトのビットがどこに保存されているかは、ユーザーにとっては重要ではありません。オブジェクトが参照型であるか値型であるかは問題になりますが、ガベージコレクションの動作を最適化するまで、オブジェクトの保存場所を気にする必要はありません。
参照型は現在の実装では常にヒープに割り当てられますが、値型はスタックに割り当てられますが、必ずしもそうではありません。値型は、参照型に含まれておらず、レジスタに割り当てられていない、ボックス化されていないエスケープしないローカル変数または一時変数である場合にのみスタックに割り当てられます。
見逃したものはありますか?
もちろん、このトピックに関するEric Lippertの投稿にリンクしていなければ、私は気が進まないでしょう。
「すべてのVALUEタイプがスタックに割り当てられます」は非常に間違っています。構造体変数canメソッド変数としてスタック上に存在します。ただし、タイプそのタイプで有効のフィールド。フィールドの宣言型がクラスの場合、値はそのオブジェクトのpartとしてヒープ上にあります。フィールドの宣言型が構造体である場合、フィールドはその構造体の一部ですwhere-everその構造体は存続します。
メソッド変数canがヒープ上にある場合、captured(lambda/anon-method)、または(たとえば)イテレーターブロックの一部である場合。
stack
は、local variables
およびparameters
を保存するためのメモリブロックです。関数の開始と終了に応じて、スタックは論理的に拡大および縮小します。
次の方法を検討してください。
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の使用が完了するまでコレクションの対象にならないようにします。
値型インスタンス(およびオブジェクト参照)は、変数が宣言された場所に存在します。インスタンスがクラス型内のフィールドまたは配列要素として宣言された場合、そのインスタンスはヒープ上に存在します。
簡単な対策
値の型はスタックに紐付けられます。これは、未来派のデータ構造に割り当てることができる実装上の詳細です。
したがって、値と参照型の仕組みを理解する方が良いです、値型は値によってコピーされます。つまり、値型をparamとしてFUNCTIONに渡すと、自然によってコピーされるので、完全に新しいコピーが作成されます。
参照タイプは参照によって渡されます(また、将来のバージョンでは参照が再びアドレスを保存することを考慮しないでください。他のデータ構造に保存される可能性があります。)
あなたの場合
myIntは、参照型をオフコースするクラスにカプセル化されるintであるため、「THE HEAP」に格納されるクラスのインスタンスに関連付けられます。
eRIC LIPPERTSが書いたブログを読み始めることができます。
オブジェクトが作成されるたびに、ヒープと呼ばれるメモリ領域に入ります。 intやdoubleなどのプリミティブ変数は、ローカルメソッド変数の場合はスタックに、メンバー変数の場合はヒープに割り当てられます。メソッドでは、メソッドが呼び出されるとローカル変数がスタックにプッシュされ、メソッド呼び出しが完了するとスタックポインターがデクリメントされます。マルチスレッドアプリケーションでは、各スレッドに独自のスタックがありますが、同じヒープを共有します。このため、ヒープスペースでの同時アクセスの問題を回避するために、コードに注意を払う必要があります。スタックはスレッドセーフです(各スレッドには独自のスタックがあります)が、コードによる同期で保護されない限り、ヒープはスレッドセーフではありません。
このリンクも便利です http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/
mはMyClassのオブジェクトへの参照であるため、mはメインスレッドのスタックに格納されますが、MyClassのオブジェクトはヒープに格納されます。したがって、myIntとmyStringはヒープに格納されます。 mは参照(メモリへのアドレス)であり、メインスタック上にあることに注意してください。 mの割り当てが解除されると、GCはヒープからMyClassオブジェクトをクリアします。詳細については、この記事の4つのパートすべてをお読みください https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking- in-net-part-i /