堆栈和堆分配

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

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

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

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

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

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

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

所以,最少10个堆。

简而言之, 是的

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

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

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

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

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

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

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

进程中存在多少堆。

通常每个过程1个。

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

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

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

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