14

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

4

7 回答 7

16

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

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

于 2009-10-13T16:21:02.697 回答
13

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

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

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

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

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

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

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

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

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

于 2012-08-22T15:27:08.723 回答
11

当一个人在构建编译器时,理解这种区别当然是有帮助的。

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

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

于 2009-10-13T16:44:58.863 回答
4

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

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

于 2009-10-13T16:22:40.913 回答
3

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

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

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

于 2009-10-13T16:19:20.537 回答
0

重要的区别在于引用类型和值类型。“值类型在堆栈上,引用类型在堆上”是不正确的。Jon Skeet和Eric Lippert都写过方面的文章。

于 2009-10-13T16:54:45.443 回答
0

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

Dim originalClaim As ClaimBE
Dim workingClaim As ClaimBE

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

originalClaim = BLL.GetClaim()
workingClaim = originalClaim

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

于 2010-11-03T20:37:55.730 回答