.NET中的堆栈与堆

在您的实际编程经验中,STACK和HEAP的这些知识如何在现实生活中拯救您? 来自战壕的故事? 或者这个概念是否适合填写编程书籍并有利于理论?

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

多年来我分析了许多内存转储,发现知道两者之间的内部和差异非常有用。 其中大部分都是OutOfMemory条件和不稳定的应用程序。 在查看转储时,使用WinDbg绝对需要这些知识。 在研究内存转储时,了解内核/用户模式进程与CLR之间如何分配内存至少可以告诉您从何处开始分析。

例如,让我们来看一个OOM案例:你在堆大小,工作集,私有内存,共享内存,虚拟内存,提交内存,句柄和线程中看到的分配内存可以作为从哪里开始的重要指标。

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

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

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

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

我有一个客户端,Citrix服务器因OutOfMemory问题而崩溃,不稳定,响应速度慢,当他们的应用程序在多个会话中运行时。 在查看转储(我无法访问服务器)之后,我看到该应用程序实例使用了700多个线程! 知道线程堆栈分配,允许我关联OOM是由高线程使用引起的。

简而言之,因为我为自己的“角色”所做的一切,所以拥有它是非常宝贵的知识。 当然即使你没有调试内存转储它也不会受到伤害!

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

就个人而言,我在编码的所有年份(仅基于CLR)中从未想过堆栈或堆栈。

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

以下是一些关于内存管理中的各种问题如何影响C#语言和CLR的设计和实现的文章:

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

如果您只是构建平均业务应用程序,我认为这很重要,我认为大多数.NET程序员都是。

我看过的书只是提到了堆栈和堆积,好像记住这个事实是一件具有重大意义的事情。

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

我觉得理解如何使用.NET框架(以及大多数其他语言)至关重要。 我从不聘请那些对堆栈上的内存使用情况没有清楚了解的人。

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

重要的区别在于引用类型和值类型。 “值类型进入堆栈,引用类型进入堆栈”并不是真的。 Jon Skeet写过关于这一点的内容 , Eric Lippert也是如此。

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

Dim originalClaim As ClaimBE Dim workingClaim As ClaimBE 

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

 originalClaim = BLL.GetClaim() workingClaim = originalClaim 

因此两个引用类型指向相同的值类型。 梦魇避免了。