.NET 中的堆栈与堆 [关闭]

Posted

技术标签:

【中文标题】.NET 中的堆栈与堆 [关闭]【英文标题】:Stack vs. Heap in .NET [closed] 【发布时间】:2010-12-06 09:16:57 【问题描述】:

在您的实际编程经验中,这些关于 STACK 和 HEAP 的知识如何在现实生活中真正拯救了您?战壕里有什么故事吗?或者这个概念对填满编程书籍和理论有好处吗?

【问题讨论】:

这更像是一个讨论话题,而不是一个有真实答案的问题;考虑将其切换到 wiki。 鉴于迄今为止发布的答案,这似乎更像是一场讨论。这个问题可以回答“概念没用”或“概念有用,这里有一个例子”。没有具体示例的“概念有用”实际上并不能回答问题。 看这篇文章讨论 .net 的堆栈和堆 ***.com/questions/12727821/… 【参考方案1】:

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

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

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

【讨论】:

我同意你的观点,但你确实没有提供一个很好的例子来说明堆栈和堆的知识。我有兴趣学习新东西:) 我同意 leppie 的观点,引用类型和值类型之间的区别非常重要,但是无论它们最终是在堆栈还是堆上......你还没有说服我为什么它会如此重要. 好吧,我通常会笼统地问,并尝试让应聘者向我解释差异。这已成为我理解水平的基准之一——我觉得知道 .NET 中的内存分配如何工作的人至少愿意并且能够学习任何其他需要的东西。我认为您需要了解 1) 堆栈,一般而言,2) 堆,一般而言,3) 引用类型如何工作,4) 值类型如何工作,5) 使用 ref/out 传递参数,以及它是如何工作的值不同,尤其是引用类型(不是堆栈/堆,而是半相关的) Eric Lippert 当然是一个反对的声音,他认为引用类型和值类型之间的区别远不止堆栈与堆(他将其描述为实现细节)。 blogs.msdn.com/ericlippert/archive/2009/04/27/… @kcbeard 这真的很大程度上取决于你每天在做什么。我从事技术计算工作,因此内存管理和与之相关的问题几乎是每天都在关注的问题。对于典型的 LOB 应用程序,您可能永远不会关心。但是,我仍然认为要全面理解这些重要知识,特别是因为它在处理互操作代码等时也起着重要作用。当然,本机代码使其更重要的是理解,因为托管代码隐藏了很多这种复杂性,但是最终,线条有点模糊......【参考方案2】:

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

就我个人而言,在我多年的编码(仅基于 CLR)中,我从未考虑过堆栈或堆。

【讨论】:

mmm - 如果不了解堆栈和堆,就很难理解引用类型与值类型的语义(尤其是它们背后的原因)。 也许更好的问题是:“解释为什么 value::reference != stack::heap”。 :) @Reed,我完全不同意。使用堆与堆栈来解释引用与值类型语义通常会导致混淆和虚假信息。 @ReedCopsey 参考 vs 值类型与堆栈和堆无关。虽然在某些情况下甚至 MSDN 都重复了这个概念,但这完全是错误的。例如,值类型可以存储在堆上(固定句柄、装箱等)、堆栈上、寄存器中(我听过很多人说“C# 很慢,因为它是基于堆栈的”,这甚至都不好笑不再),甚至是大对象堆。无论如何,它是一个实现细节,如果 .NET 运行时决定,它可以对这些值做任何想做的事情——这就是它的工作。堆栈/堆是关于范围(现在),而不是关于数据类型。 关于上述问题的优秀文章(如果您还没有阅读过):blogs.msdn.com/b/ericlippert/archive/2009/04/27/… 和 blogs.msdn.com/b/ericlippert/archive/2009/05/04/…【参考方案3】:

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

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

【讨论】:

【参考方案4】:

在构建编译器时理解它们之间的区别当然是有帮助的。

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

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

【讨论】:

【参考方案5】:

重要的区别在于引用类型和值类型。 “值类型在堆栈上,引用类型在堆上”是不正确的。 Jon Skeet 写了about this 和Eric Lippert。

【讨论】:

【参考方案6】:

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

Dim originalClaim As ClaimBE
Dim workingClaim As ClaimBE

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

originalClaim = BLL.GetClaim()
workingClaim = originalClaim

因此两个引用类型指向同一个值类型。噩梦避免了。

【讨论】:

你的故事与栈和堆无关;它只是价值与参考。正如其他人所说,值类型的区别在于能够存储在堆栈上,但这就是关系结束的地方。 originalClaimworkingClaim 是存储在堆栈还是堆中与您的故事无关。 @Gabe 更有趣的是,引用类型也可以放在堆栈上。它不会经常发生(一个例子是不安全上下文中的 stackalloc 关键字),但是如果认为值得麻烦的话,这可能会在未来的 .NET 版本中发生变化 - 这在执行分配繁重的操作时将是一个巨大的帮助在给定范围内使用分配的对象 - 无需将其放在堆上供 GC 稍后收集,只需使用堆栈、推送、弹出、完成,不需要 GC。【参考方案7】:

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

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

以一个OOM案例为例: 您在堆大小、工作集、私有内存、共享内存、虚拟内存、已提交内存、句柄和线程中看到的已分配内存可能是一个重要的指标。

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

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

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

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

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

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

【讨论】:

为了完整起见,我想补充一点:虽然每个线程确实有一个 1 MiB 的默认堆栈,但这并不意味着内存已提交(它不是,至少不是在 64位 Windows 7 与 .NET 框架 4)。我看到的默认值更像是默认提交的 4 kiB。所以它确实需要 1 MiB 的虚拟地址空间,但不需要 1 MiB 的物理内存(无论是页面文件、其他内存映射文件还是实际 RAM)。在 64 位系统上,您可以拥有数千个堆栈,但仍然只占用几兆的“真实”内存。事实上,我已经看到托管堆分配 TiB,而只提交几兆。 感谢您注意需要澄清 - 我已经编辑了我的回复以明确调用虚拟内存而不是已提交。

以上是关于.NET 中的堆栈与堆 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C++ 堆栈与堆分配

Go 中结构的堆栈与堆分配,以及它们与垃圾收集的关系

巨大的内存分配:堆栈与堆

java堆栈堆栈的区别

Java中的堆和栈以及堆栈的区别

java 堆栈 理解