28

我正在研究内存模型,并且正在努力了解进程中存在多少堆。

因此,如果我们有 1 个进程,其中有 5 个线程,我是否正确地说我们有 5 个堆栈和 1 个堆?

如果是这样,线程是否可以访问彼此的堆栈(或者这正是它们具有单独堆栈以防止损坏的原因),如果只有 1 个堆,那么显然它们都访问该堆,因此需要用多个线程锁定?我是否正确理解这一点?

4

4 回答 4

40

是的,每个线程都有自己的堆栈。这是非常必要的,堆栈会跟踪方法完成后返回的位置,并存储返回地址。由于每个线程都执行自己的代码,因此它们需要自己的堆栈。局部变量和方法参数也存储在那里,使它们(通常)是线程安全的。

堆的数量是一个更复杂的细节。您正在为垃圾收集堆计数 1。从实现的角度来看,这并不完全正确,三代堆加上大对象堆在逻辑上是不同的堆,最多加起来四个。当您分配过多时,此实现细节开始变得重要。

在托管代码中您不能完全忽略的另一个是存储静态变量的堆。它与 AppDomain 相关联,静态变量与 AppDomain 一样存在。在 .NET 文献中通常称为“加载程序堆”。它实际上由 3 个堆(高频堆、低频堆和存根堆)组成,jitted 代码和类型数据也存储在那里,但这已经很重要了。

忽略列表的下方是本机代码使用的堆。其中两个很容易从元帅级中看到。有一个默认进程堆,Windows 从它分配,Marshal.AllocHGlobal() 也是如此。还有一个单独的堆,COM 存储数据,Marshal.AllocCoTaskMem() 从中分配。最后,您与之互操作的任何本机代码都将拥有自己的堆以支持其运行时支持。此类代码使用的堆数量仅受加载到您的进程中的本机 DLL 数量的限制。所有这些堆都存在,你几乎不会直接处理它们。

因此,最少 10 个堆。

于 2012-06-26T00:12:19.850 回答
12

简而言之,的。

进程中的所有线程共享同一个堆,因此它们可以交换数据。每个线程都有自己的堆栈,该堆栈与该线程上的当前代码执行相关。

一个非常好的线程资源在这里:http ://www.albahari.com/threading/

线程类似于运行应用程序的操作系统进程。正如进程在计算机上并行运行一样,线程在单个进程中并行运行。进程彼此完全隔离;线程只有有限程度的隔离。特别是,线程与在同一应用程序中运行的其他线程共享(堆)内存。这部分是线程有用的原因:例如,一个线程可以在后台获取数据,而另一个线程可以在数据到达时显示数据。

于 2012-06-25T13:08:09.547 回答
4

线程是在单个进程的相同虚拟地址空间中同时运行的独立指令流。堆是系统提供给每个进程供其私人使用的一大块内存。进程可以调整它们的堆大小,并且可以根据需要使用堆空间。线程可以协作使用这个堆空间,也可以分配额外的私有内存区域,称为线程本地存储(TLSes)。

由于所有线程共享相同的虚拟地址空间,它们可以直接访问彼此的堆栈内存。这意味着一个线程可以将其堆栈上的变量作为参数传递给在其他线程中运行的函数。但是线程堆栈仍然是独立的,因为一个线程从不将值推送或弹出到其他线程的堆栈中,而只会将其推送到自己的堆栈空间中。由于 x86 和 x86-64 上的堆栈向下增长,因此每个线程的堆栈内存底部都有一个特殊的页面——所谓的保护页面。如果在操作堆栈时到达保护页面,则会发生堆栈错误。

在 C 和 C++ 等非托管语言中,进程内存的每个部分都可以通过使用指针随意访问。一个线程可以完全弄乱另一个线程堆栈的内容,从而使第二个线程(以及整个进程)崩溃。在 C# 中,这些事情不会发生在unsafe块之外,因为堆栈是由 CLR 管理的。

于 2012-06-25T14:01:11.683 回答
2

这是实现定义的,但让我们谈谈最流行的现代操作系统,因为您添加了 C# 标记。

一个进程中存在多少堆。

通常每个进程 1 个。

因此,如果我们有 1 个进程,其中有 5 个线程,我是否正确地说我们有 5 个堆栈和 1 个堆?

是的。每个线程立即消耗 1MB 的虚拟地址空间用于线程堆栈。

如果是这样,线程是否可以访问彼此的堆栈(或者这正是它们具有单独堆栈以防止损坏的原因),如果只有 1 个堆,那么显然它们都访问该堆,因此需要用多个线程锁定?我是否正确理解这一点?

是的,现代环境是非常好的沙盒环境,因此您不能直接从另一个线程访问其他线程堆栈。

于 2012-06-25T13:14:46.240 回答