実際のプログラミング経験において、この STACK と HEAP の知識は実際にどのように役に立ちましたか? 塹壕の話は?それとも、この概念はプログラミングの本を埋めるのに適していて、理論に適しているのでしょうか?
7 に答える
参照型と値型のセマンティクス間の .NET の違いは、把握するよりもはるかに重要な概念です。
個人的には、何年にもわたるコーディング (CLR ベースのみ) の中で、スタックやヒープについて気にしたことはありません。
私にとって、それは「開発者/プログラマー」と「職人」の違いです。誰でもコードを書くことを学び、理由や方法がわからないあなたのために、物事が「魔法のように起こる」方法を見ることができます。自分がしていることに本当に価値があるためには、使用しているフレームワークについてできる限り多くのことを知ることが非常に重要だと思います。これは単なる言語ではなく、自分の能力に最適なアプリケーションを作成するために活用するフレームワークであることを忘れないでください。
私は何年にもわたって多くのメモリ ダンプを分析してきました。これらのほとんどは、OutOfMemory 状態と不安定なアプリケーションです。この知識は、ダンプを見るときに WinDbg を使用するために絶対に必要です。メモリ ダンプを調査する場合、カーネル/ユーザー モード プロセスと CLR の間でメモリがどのように割り当てられているかを知っていれば、少なくともどこから分析を開始すればよいかがわかります。
たとえば、OOM のケースを考えてみましょう。ヒープ サイズ、ワーキング セット、プライベート メモリ、共有メモリ、仮想メモリ、コミット済みメモリ、ハンドル、およびスレッドに表示される割り当てられたメモリは、どこから始めればよいかを示す大きな指標となります。
CLR が使用する約 8 つの異なるヒープがあります。
- ローダー ヒープ: CLR 構造体と型システムが含まれています
- 高頻度ヒープ: statics、MethodTables、FieldDescs、インターフェイス マップ
- 低頻度ヒープ: EEClass、ClassLoader、およびルックアップ テーブル
- スタブ ヒープ: CAS、COM ラッパー、P/Invoke のスタブ
- 大きなオブジェクト ヒープ: 85k バイト以上を必要とするメモリ割り当て
- GC ヒープ: アプリ専用のユーザー割り当てヒープ メモリ
- JIT コード ヒープ: mscoreee (実行エンジン) によって割り当てられたメモリとマネージ コード用の JIT コンパイラ
- プロセス/ベース ヒープ: 相互運用/管理されていない割り当て、ネイティブ メモリなど
どのヒープに高い割り当てがあるかを見つけると、メモリの断片化、マネージ メモリ リーク、相互運用/アンマネージ リークなどがあるかどうかがわかります。
アプリが使用する各スレッドに 1MB (x86 の場合)/4MB (x64 の場合) のスタック領域が割り当てられていることを知っていると、100 個のスレッドがある場合、仮想メモリの使用量が 100MB 増えることを思い出します。
OutOfMemory の問題で Citrix サーバーがクラッシュするクライアントがありました。アプリケーションが複数のセッションで実行されていると、不安定になり、応答が遅くなります。ダンプを確認した後 (サーバーにアクセスできませんでした)、アプリのそのインスタンスで 700 を超えるスレッドが使用されていることがわかりました。スレッド スタックの割り当てを知ることで、スレッドの使用率が高いために発生した OOM を関連付けることができました。
要するに、自分の「役割」のためにやっているからには、非常に貴重な知識です。もちろん、メモリ ダンプをデバッグしていなくても問題はありません。
コンパイラを構築する際に、違いを理解することは確かに役に立ちます。
メモリ管理のさまざまな問題が C# 言語と CLR の設計と実装に与える影響について、私が書いたいくつかの記事を次に示します。
http://blogs.msdn.com/ericlippert/archive/tags/Memory+Management/default.aspx
ほとんどの .NET プログラマーがそうであるように、平均的なビジネス アプリケーションを構築しているだけであれば、それは問題ではないと思います。
私が見た本は、この事実を記憶することが非常に重要であるかのように、スタックとヒープについて言及しているだけです。
個人的には、これは採用しようとしているすべての人に尋ねる数少ない技術的な質問の 1 つです。
.NET フレームワーク (および他のほとんどの言語) の使用方法を理解するには、これが重要であると感じています。スタックとヒープのメモリ使用量を明確に理解していない人を雇うことはありません。
これを理解しないと、ガベージ コレクターを理解したり、.NET のパフォーマンス特性を理解したり、その他多くの重要な開発上の問題を理解したりすることはほとんど不可能です。
重要な違いは、参照型と値型の間です。「値型はスタックに、参照型はヒープに」というのは正しくありません。Jon Skeet がこれについて書いており、Eric Lippertもそうです。
クレーム全体のデータを含むクレーム エンティティ (ビジネス オブジェクト) がありました。アプリケーションの要件の 1 つは、ユーザーが変更したすべての値の監査証跡を作成することでした。データベースに 2 回アクセスせずにこれを行うには、フォーム内のオリジナル クレーム エンティティとワーキング クレーム エンティティを維持します。ユーザーが [保存] をクリックすると、ワーキング クレーム エンティティが更新され、元のクレーム エンティティのプロパティと対応するワーキング クレーム エンティティのプロパティを比較して、何が変更されたかを判断します。ある日、compare メソッドが違いを見つけられないことに気付きました。これは、スタックとヒープに関する私の理解が私のリアエンドを救った場所です(特に値型と参照型)。メモリ内の同じオブジェクトのコピーを維持する必要があったため、開発者は単純に 2 つのオブジェクトを作成しました。
Dim originalClaim As ClaimBE
Dim workingClaim As ClaimBE
次に、ビジネス レイヤー メソッドを呼び出してクレーム オブジェクトを返し、同じ claimBE を両方の変数に割り当てました。
originalClaim = BLL.GetClaim()
workingClaim = originalClaim
したがって、2 つの参照型が同じ値型を指しています。悪夢は回避されました。