.NET 中的堆栈与堆

发布于 2024-08-07 03:08:56 字数 1432 浏览 2 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

灼痛 2024-08-14 03:08:56

.NET 中引用类型和值类型的语义之间的区别是一个需要掌握的更重要的概念。

就我个人而言,在我多年的编码生涯中,我从未费心去思考堆栈或堆(只是基于 CLR)。

The distinction in .NET between the semantics of reference types and value types, is a much more important concept to grasp.

Personally, I have never bothered thinking about the stack or heap in all my years of coding (just CLR based).

苏璃陌 2024-08-14 03:08:56

对我来说,这就是“开发人员/程序员”和“工匠”之间的区别。任何人都可以学习编写代码,并看到事情如何“神奇地发生”,而你却不知道为什么/如何发生。为了让你所做的事情真正有价值,我认为尽可能多地了解你正在使用的框架非常重要。请记住,它不仅仅是一种语言,它还是一个框架,您可以利用它来根据自己的能力创建最佳应用程序。

多年来我分析了许多内存转储,发现了解两者之间的内部结构和差异非常有帮助。其中大多数都是内存不足和应用程序不稳定的情况。在查看转储时使用 WinDbg 绝对需要这些知识。在调查内存转储时,了解内核/用户模式进程和 CLR 之间的内存分配方式至少可以告诉您从哪里开始分析。

我们以 OOM 为例:
您在堆大小、工作集、私有内存、共享内存、虚拟内存、提交内存、句柄和线程中看到的已分配内存可以作为从何处开始的重要指标。

CLR 使用大约 8 种不同的堆:

  1. 加载器堆:包含 CLR 结构和类型系统
  2. 高频堆:静态、MethodTables、FieldDescs、接口映射
  3. 低频堆:EEClass、ClassLoader 和查找表
  4. 存根堆:CAS、COM 的存根包装器、P/Invoke
  5. 大对象堆:需要超过 85k 字节的内存分配
  6. GC 堆:用户分配的应用程序私有堆内存
  7. JIT 代码堆:由 mscoreee(执行引擎)和 JIT 编译器为托管代码
  8. 进程/基 分配的内存堆:互操作/非托管分配、本机内存等

查找哪些堆具有高分配可以告诉我是否存在内存碎片、托管内存泄漏、互操作/非托管泄漏等。

知道您有 1MB(在 x86 上)/ 4MB(在x64) 为您的应用程序使用的每个线程分配的堆栈空间提醒我,如果我有 100 个线程,您将有额外的 100MB 虚拟内存使用量。

我有一个客户,当他们的应用程序在多个会话中运行时,Citrix 服务器因内存不足问题而崩溃、不稳定、响应缓慢。查看转储后(我无法访问服务器),我发现该应用程序实例正在使用超过 700 个线程!了解线程堆栈分配后,我可以将 OOM 与高线程使用率引起的相关联。

简而言之,由于我为我的“角色”所做的事情,所以拥有的知识是无价的。当然,即使您不调试内存转储,也没有什么坏处!

To me it is the difference between being a "developer/programmer" and a "craftsman". Anyone can learn to write code and see how things just "magically happen" for you not knowing why/how. To really be valuable at what you do, I think there is a great importance to find out as much as you can about the Framework you're using. Remember it's not just a language, it's a Framework that you leverage to create the best application to your abilities.

I've analyzed many memory dumps over the years and found it extremely helpful knowing the internals and differences between the two. Most of these have been OutOfMemory conditions and unstable applications. This knowledge is absolutely necessary to use WinDbg when looking at dumps. When investigating a memory dump, knowing how memory is allocated between the kernel/user-mode process and the CLR can at least tell you where to begin your analysis.

For example, let's take an OOM case:
The allocated memory you see in the Heap Sizes, Working Set, Private Memory, Shared Memory, Virtual Memory, Committed Memory, Handles, and Threads can be a big indicator of where to start.

There about 8 different heaps that the CLR uses:

  1. Loader Heap: contains CLR structures and the type system
  2. High Frequency Heap: statics, MethodTables, FieldDescs, interface map
  3. Low Frequency Heap: EEClass, ClassLoader and lookup tables
  4. Stub Heap: stubs for CAS, COM wrappers, P/Invoke
  5. Large Object Heap: memory allocations that require more than 85k bytes
  6. GC Heap: user allocated heap memory private to the app
  7. JIT Code Heap: memory allocated by mscoreee (Execution Engine) and the JIT compiler for managed code
  8. Process/Base Heap: interop/unmanaged allocations, native memory, etc

Finding what heap has high allocations can tell me if I have memory fragmentation, managed memory leaks, interop/unmanaged leaks, etc.

Knowing that you have 1MB (on x86)/ 4MB (on x64) of stack space allocated for each thread that your app uses reminds me that if I have 100 threads you will have an additional 100MB of virtual memory usage.

I had a client that had Citrix servers crashing with OutOfMemory problems, being unstable, slow responsiveness when their app was running on it in multiple sessions. After looking at the dump (I didn't have access to the server), I saw that there were over 700 threads being used by that instance of the app! Knowing the thread stack allocation, allowed me to correlate the OOMs were caused by the high thread usage.

In short, because of what I do for my "role", it is invaluable knowledge to have. Of course even if you're not debugging memory dumps it never hurts either!

荭秂 2024-08-14 03:08:56

当人们构建编译器时,理解其中的区别当然是有帮助的。

以下是我撰写的几篇文章,介绍内存管理中的各种问题如何影响 C# 语言和 CLR 的设计和实现:

http://blogs.msdn.com/ericlippert/archive/tags/Memory+Management/default.aspx

It certainly is helpful to understand the distinction when one is building compilers.

Here are a few articles I've written about how various issues in memory management impact the design and implementation of the C# language and the CLR:

http://blogs.msdn.com/ericlippert/archive/tags/Memory+Management/default.aspx

岁月打碎记忆 2024-08-14 03:08:56

如果您只是构建普通的业务应用程序(我认为大多数 .NET 程序员都是如此),我认为这并不重要。

我看过的书只是顺便提到了栈和堆,好像记住这个事实是非常重要的。

I don't think it matters if you're just building average business applications, which I think most .NET programmers are.

The books I've seen just mention stack and heap in passing as if memorizing this fact is something of monumental importance.

属性 2024-08-14 03:08:56

就我个人而言,这是我向每个要雇用的人提出的极少数技术问题之一。

我认为了解如何使用 .NET 框架(以及大多数其他语言)至关重要。我从来不会雇用对堆栈和堆上的内存使用情况没有清晰了解的人。

如果不了解这一点,就几乎不可能了解垃圾收集器、了解 .NET 性能特征以及许多其他关键的开发问题。

Personally, this is one of the very few technical questions that I ask every person I'm going to hire.

I feel that it is critical to understanding how to use the .NET framework (and most other languages). I never hire somebody who doesn't have a clear understanding of memory usage on the stack vs. the heap.

Without understanding this, it's almost impossible to understand the garbage collector, understand .NET performance characteristics, and many other critical development issues.

夏九 2024-08-14 03:08:56

重要的区别在于引用类型和值类型之间。 “值类型位于堆栈上,引用类型位于堆上”是不正确的。 Jon Skeet 曾写过有关此内容埃里克·利珀特

The important distinction is between reference types and value types. It's not true that "value types go on the stack, reference types go on the heap". Jon Skeet has written about this and so has Eric Lippert.

甜味超标? 2024-08-14 03:08:56

我们有一个索赔实体(业务对象),其中包含整个索赔的数据。该应用程序的要求之一是创建用户更改的每个值的审计跟踪。为了在不两次访问数据库的情况下实现这一点,我们将在表单中维护原始索赔实体和工作索赔实体。当用户单击“保存”时,工作声明实体将得到更新,然后我们将原始声明实体属性与相应的工作声明实体属性进行比较,以确定更改的内容。有一天,我们注意到我们的比较方法从未发现差异。这就是我对堆栈和堆的理解拯救了我的后端(特别是值类型与引用类型)。因为我们需要在内存中维护同一对象的副本,所以开发人员只需创建两个对象,

Dim originalClaim As ClaimBE
Dim workingClaim As ClaimBE

然后调用业务层方法来返回声明对象,并将相同的 ClaimBE 分配给两个变量,

originalClaim = BLL.GetClaim()
workingClaim = originalClaim

因此两个引用类型指向相同的值类型。噩梦得以避免。

We had a Claim Entity (business Object) which contained data for an entire claim. One of the requirements of the application was to create an audit trail of every single value changed by the user. In order to this without hitting the database twice we would maintain Original Claim Entity in the form and a Working Claim Entity. The Working Claim Entity would get updated when the user clicked Save and we would then compare the Original Claim Entity properties with corresponding Working Claim Entity properties to determine what changed. One day we noticed hey our compare method is never finding a difference. This is where my understanding of the Stack and Heap saved my rear end (specifically value types vs reference types). Because we needed to maintain to copies of the same object in memory the developer simply created two objects

Dim originalClaim As ClaimBE
Dim workingClaim As ClaimBE

then called the business layer method to return the claim object and assigned the same claimBE to both variables

originalClaim = BLL.GetClaim()
workingClaim = originalClaim

hence two reference types pointing to the same value type. Nightmare averted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文