堆栈和堆分配

时间:2012-06-25 13:04:19

标签: c# multithreading

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

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

如果是这样,线程可以访问彼此的堆栈(或者这就是为什么他们有单独的堆栈,以防止损坏),如果只有1个堆,那么显然他们都访问这个堆,因此需要锁定多线程?我能正确理解吗?

4 个答案:

答案 0 :(得分:38)

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

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

另一个在托管代码中无法完全忽略的是存储静态变量的堆。它与AppDomain相关联,只要AppDomain存在,静态变量就会生效。在.NET文献中通常称为“加载器堆”。它实际上由3堆(高频率,低频率和存根堆)组成,jitted代码和类型数据也存储在那里但是它已经达到了实质性。

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

所以,最少10个。

答案 1 :(得分:12)

简而言之,

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

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

  

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

答案 2 :(得分:4)

线程是在单个进程的同一虚拟地址空间中同时运行的独立指令流。堆是一大块内存,系统为每个进程提供私有用。进程可以调整其堆大小,并可以在他们认为必要时使用堆空间。线程可以协作使用此堆空间,还可以分配称为线程本地存储(TLS)的其他专用内存区域。

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

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

答案 3 :(得分:2)

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

  

进程中存在多少堆。

每个流程通常为1个。

  

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

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

  

如果是这样,线程可以访问彼此的堆栈(或者这就是为什么他们有单独的堆栈,以防止损坏),如果只有1个堆,那么显然他们都访问这个堆,因此需要锁定多线程?我能正确理解吗?

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