オブジェクトインスタンスに属するメソッド(およびその変数)は、スタックまたはヒープに移動しますか?
Ex
Main()
{
Myclass Myobj = new Myclass();
Myobj.Doit();
}
class Myclass
{
Void Doit()
{
Int myint = 5;
}
}
私は主にc#を使用していますが、答えは言語に依存しないと思います。
CLR標準はスタックまたはヒープを必要としないので、最初にそれを邪魔にならないようにします。しかし、紙に実装されたC#はあまり役に立ちません。ここでは、Microsoft C#やMono C#のように、「実際に」コードを実行できる実装について説明します。いずれにしても、メソッドとローカル変数は、理解する必要があるクラスやオブジェクトインスタンスとの概念的な関係を持っています。これは、C#に固有ではないため、コンピューター言語全般で同じです。
インスタンスメソッドは、静的メソッドと同じ方法で格納されます。
メソッドはcode
(CLRではバイトコード)の一部であり、アセンブリを構成するコンパイルされた命令、低レベルCLRオペコードです。したがって、C#モデルでは、それらはヒープの一部ではなく、ヒープはdata
用です。 (下部の警告脚注を参照してください)。オブジェクト指向ではない言語には、単純な関数があります。これらの言語では、関数は引数を受け取る単なるコードです。 CLRでは、プレーン関数は静的メソッド(またはクラスメソッド)です。関数とメソッドの唯一の違いは、構文糖衣が関数を所有しているように見せることですが、そのオブジェクトは実際には最初の「暗黙の」引数(this)であり、ほとんどの言語ではメソッド呼び出しの前に来ます。 obj.Foo()
。関数の最初の引数は、引き続き命令によってロードされる必要があります。 CLR MSILでは、静的メソッドの場合を除いて、this
はldarg.0
によってロードされます。したがって、obj.Foo(arg)
はFoo(obj,arg)
と同等です。
クラスをコンパイルすると、コンパイラはメソッドを表す一連の命令を発行し、コードセグメントにパックします。
メソッドとオブジェクトの関係は、実際にはクラスとオブジェクトの関係に似ています。メソッドはクラスの一部です(つまり、オブジェクトの一部ではなくtypeの一部です。メソッドは静的データに似ていますが、「メソッドデータ」はコードである。静的フィールドと同様に、メソッドはオブジェクトインスタンスの前にも存在せずに存在し、オブジェクトのタイプの一部にすぎない。文字列のインスタンスが1,000,000あるかもしれないが、どこかにstring::Concat(string)
の単一のコピーがある。
メソッドのローカル変数に関する限り、それらはメソッドが呼び出されるまで存在しません。 CLRメソッドの最初に、呼び出しフレームはそのメソッドのすべてのローカル変数のスペースを確保します。これらは事前にわかっていますが、メソッドが実行されるまでは形式表記であり、その後は実際のデータアドレスになります。命令は、割り当てる容量をCLRに指示します。ローカル変数の値は、概念的および実際的にスタック上にありますが、レジスターにマップされています。変数はメソッドのスコープに対して「ローカル」であり、メソッドが戻ると消えます(ただし、参照するオブジェクトはそうでない場合があります)。 CLRでそれらを処理するための手順があります。 DoIt()
メソッドのMSIL(CLRのILアセンブリ)の大まかなサンプルを見てみましょう。
.method void MyClass::Doit()
{
.locals init([0]int myint) // declares the locals for the method
ldc.i4.5
stloc 'myint' // initialize myint to 0
ret
}
ローカルは、実際にCPUレジスタJITコンパイラによって積極的にマッピングされるため、CLRがバイトコードを解釈または検証しているときに、ほとんどがスタックに存在します。実際には、ローカルはレジスタに存在しますが、低レベルのハードウェア上のスタックに波及します。
最後に、どのコンピューター言語でも、3つの主要な構文グループがあります。
警告:ランタイムシステムは一般にCまたはC++で実装されます。 CLRアセンブリとそのメソッドは、CLRの記述に使用されるホスト言語のランタイムヒープに読み込まれます。ただし、概念的には、これはCLR内でアクセスしている「ヒープ」とは異なるヒープです。
一般に、プログラムコードは、「スタック」や「ヒープ」と言ったときの意図とは異なる領域に存在します。 C#について言及しましたが、言語タグはないので、典型的な最新のデスクトップOSがこれを低レベルで処理する方法について説明します。
プログラムが読み込まれると、コードとデータという2つの主要なメモリ領域があります。 OSは、プログラムをロードするのに十分なメモリを割り当てます。これには、実行可能コード、静的データ(例:定数)、およびコンパイラに含まれるすべてのbootstrapコードが含まれます(何かがmain
を呼び出す必要があります) )。
この意味では、単にeverythingがOSの観点から割り当てられたヒープであるため、コードはヒープ上にあります。ただし、プログラムの観点から見ると、それはプログラムのスタックandヒープの外にあります。通常、どちらの場所にもメモリを割り当ててコードを配置することはできません(例外:次の段落)。
一部の言語では、変数がコードとして実行されるeval
またはexec
呼び出しが許可されます。これには、関数とその関数の呼び出しを含めることができます。その意味で、コードはスタックまたはヒープ内に存在する可能性がありますが、詳細は、それを可能にする言語(PerlやPHPなど)でほとんどの場合抽象化されます。この場合、コードがどこにあるかを確実に知ることは困難です。
新しいCPUがデータセグメントからのコードの実行を禁止するためのハードウェアレベルのサポートを備えているため、データセグメント(およびそのためのヒープとスタック)からのこのコードの分離は、ますます強力になっています。アイデアは、実行中のコードを変更するためにウイルスに依存するウイルスを無力化し、その結果、検出を拡散および回避するのを助けることです。プログラムが実行フラグ保護の対象である場合、コードはメモリのデータ(スタックおよびヒープ)部分から実行できません。
C#では、メモリ管理は実装の詳細です。オブジェクトがどこに移動するかは保証されません。
それを実行しているオペレーティングシステムに「スタック」が存在するという保証すらありません。
それに基づくロジックはありません。
私が私の知識を持っている記事:
http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx