堆栈与.NET中的堆

时间:2009-10-13 16:15:23

标签: c# .net vb.net stack heap

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

7 个答案:

答案 0 :(得分:16)

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

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

答案 1 :(得分:12)

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

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

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

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

  1. Loader Heap:包含CLR结构和类型系统
  2. 高频堆:静态,MethodTables,FieldDescs,界面图
  3. 低频堆:EEClass,ClassLoader和查找表
  4. Stub Heap:CAS,COM包装器,P / Invoke
  5. 的存根
  6. 大对象堆:需要超过85k字节的内存分配
  7. GC堆:用户为应用分配了专用的堆内存
  8. JIT代码堆:由mscoreee(执行引擎)分配的内存和托管代码的JIT编译器
  9. 进程/基本堆:互操作/非托管分配,本机内存等
  10. 查找具有高分配的堆可以告诉我是否存在内存碎片,托管内存泄漏,互操作/非托管泄漏等。

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

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

    简而言之,由于我为自己的角色所做的事情,这是非常宝贵的知识。当然,即使你没有调试内存转储,它也不会受到伤害!

答案 2 :(得分:11)

理解构建编译器时的区别肯定是有帮助的。

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

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

答案 3 :(得分:4)

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

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

答案 4 :(得分:3)

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

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

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

答案 5 :(得分:0)

重要的区别在于引用类型和值类型。 “值类型进入堆栈,引用类型进入堆栈”并不是真的。 Jon Skeet写了about this,因此Eric Lippert

答案 6 :(得分:0)

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

Dim originalClaim As ClaimBE
Dim workingClaim As ClaimBE
然后,

调用业务层方法返回声明对象,并为两个变量

分配相同的声明
originalClaim = BLL.GetClaim()
workingClaim = originalClaim

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