似乎每本.net书籍都在谈论值类型与引用类型,并使其指向(通常是错误地)存储每种类型(堆或栈)的状态。通常,它在前几章中介绍,是一些非常重要的事实。我认为认证考试甚至都涵盖了它。为什么(初学者).Net开发人员堆栈与堆甚至重要?您分配的东西就可以了,对吧?
似乎每本.net书籍都在谈论值类型与引用类型,并使其指向(通常是错误地)存储每种类型(堆或栈)的状态。通常,它在前几章中介绍,是一些非常重要的事实。我认为认证考试甚至都涵盖了它。为什么(初学者).Net开发人员堆栈与堆甚至重要?您分配的东西就可以了,对吧?
Answers:
我逐渐确信,这些信息被认为很重要的主要原因是传统。 在非托管环境中,区分堆栈和堆很重要,我们必须手动分配和删除我们使用的内存。现在,垃圾回收负责管理,因此他们忽略了这一点。我认为消息并没有真正得到解决,我们也不必关心使用哪种类型的内存。
正如Fede指出的那样,埃里克·利珀特(Eric Lippert)对此有一些非常有趣的事情要说:http : //blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx。
根据这些信息,您可以将我的第一段内容调整为基本上是这样的:“人们之所以包含此信息,并认为它很重要,是因为信息不正确或不完整以及过去需要这些知识。”
对于那些认为出于性能原因仍然很重要的人:如果您确实对事物进行了度量并发现它很重要,那么您将采取什么措施将某些事物从堆中移到栈中?您更有可能找到完全不同的方法来提高问题区域的性能。
似乎每本.NET书籍都在谈论值类型与引用类型,并使其指向(通常是错误地)存储每种类型(堆或栈)的状态。通常,它在前几章中介绍,是一些非常重要的事实。
我完全同意; 我一直都这样。
为什么.NET书籍为什么谈论堆栈与堆内存分配?
原因之一是因为许多人都是从C或C ++的背景来学习C#(或其他.NET语言)的。由于这些语言不会为您强制执行有关存储寿命的规则,因此您需要了解这些规则并仔细实施程序以遵循它们。
现在,了解这些规则并在C中遵循它们并不需要您了解“堆”和“堆栈”。但是,如果您确实了解数据结构的工作原理,那么通常更容易理解和遵循规则。
当写一个初学者的书是很自然的作者解释的概念以相同的顺序是他们学会了他们。这不一定对用户有意义。我最近是Scott Dorman的C#4初学者书籍的技术编辑,而我喜欢它的一件事是Scott为主题选择了一个非常合理的排序,而不是从内存管理的真正高级主题入手。
原因的另一部分是MSDN文档中的某些页面强烈强调存储注意事项。尤其是早期的MSDN文档,至今仍在使用。该文档中的许多都存在从未被删除的细微错误,您必须记住它是在历史上的特定时间针对特定受众编写的。
为什么(初学者).NET开发人员堆栈与堆甚至重要?
我认为事实并非如此。更重要的是要了解以下内容:
等等。
您分配的东西就可以了,对吧?
那是理想。
现在,在某些情况下它确实很重要。垃圾收集很棒而且相对便宜,但是它不是免费的。在周围复制小型结构相对便宜,但并非免费。在现实的性能场景中,您必须平衡收集压力的成本与过度复制的成本。在这些情况下,深入了解所有相关内存的大小,位置和实际寿命非常有帮助。
同样,在现实的互操作方案中,有必要了解堆栈中的内容和堆中的内容以及垃圾回收器可能在移动的内容。这就是C#具有“固定”,“堆栈分配”等功能的原因。
但是这些都是高级方案。理想情况下,初学者程序员无需担心这些东西。
你们都错过了重点。堆栈/堆区别之所以重要的原因在于范围。
struct S { ... }
void f() {
var x = new S();
...
}
一旦X去的范围时,所创建的对象断然消失了。那只是因为它是在堆栈上分配的,而不是在堆上分配的。方法的“ ...”部分中没有任何内容可以改变这一事实。特别是,任何赋值或方法调用都只能复制 S结构,而不能创建新的引用以使其得以存活。
class C { ... }
void f() {
var x = new C();
...
}
完全不同的故事!由于x现在位于堆上,因此x超出范围后,它的对象(即对象本身,而不是其副本)可能会继续存在。实际上,它不会继续存在的唯一方法是x是对其的唯一引用。如果“ ...”部分中的赋值或方法调用创建了其他引用,这些引用在x超出范围时仍处于“活动”状态,则该对象将继续存在。
那是一个非常重要的概念,真正理解“什么和为什么”的唯一方法是知道堆栈和堆分配之间的区别。
...
可能会导致x
转换为编译器生成的类的字段,从而超出了指定范围。就个人而言,我觉得隐式提升的想法令人反感,但是语言设计者似乎会这样做(而不是要求任何被提升的变量在其声明中都必须声明某些内容)。为了确保程序的正确性,通常必须考虑对象可能存在的所有引用。知道到例程返回时,将不会保留任何传入引用的副本。
structType foo
,则该存储位置foo
将保留其字段的内容;如果foo
在堆栈上,则其字段也是如此。如果foo
在堆上,则其字段也是如此。如果foo
位于联网的Apple II中,则其字段也是如此。相反,如果foo
是类类型,则它将包含null
或对对象的引用。foo
可以说class-type 持有对象字段的唯一情况是,如果它是类的唯一字段,并且持有对自身的引用。
至于为什么要覆盖这个主题,我同意@Kirk的观点,这是一个重要的概念,您必须理解。您对这些机制了解得越多,就可以做得更好,使出色的应用程序平稳运行。
现在,埃里克·利珀特(Eric Lippert)似乎同意您的观点,即大多数作者未正确涵盖该主题。我建议您阅读他的博客,以更好地了解其内幕。
您必须了解内存分配如何有效地使用它,即使您不必显式管理它。这几乎适用于计算机科学中的每个抽象。