C# 堆栈和堆分配

C# 堆栈和堆分配,c#,multithreading,C#,Multithreading,我正在进一步研究内存模型,并且正在努力理解一个进程中存在多少堆 所以,如果我们有一个进程,其中有5个线程,我说我们有5个堆栈和1个堆,对吗 如果是这样,线程是否可以访问彼此的堆栈(或者这就是为什么它们有单独的堆栈以防止损坏),如果只有一个堆,那么显然它们都可以访问这个堆,因此需要使用多个线程进行锁定?我理解正确吗?简言之,是 进程中的所有线程共享同一堆,因此它们可以交换数据。每个线程都有自己的堆栈,该堆栈与该线程上的当前代码执行相关 这里有一个非常好的线程资源: 线程类似于操作系统进程,您的 应

我正在进一步研究内存模型,并且正在努力理解一个进程中存在多少堆

所以,如果我们有一个进程,其中有5个线程,我说我们有5个堆栈和1个堆,对吗


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

简言之,

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

这里有一个非常好的线程资源:

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


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

一个进程中存在多少堆

通常每个过程1个

所以,如果我们有一个进程,其中有5个线程,我说我们有5个堆栈和1个堆,对吗

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

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


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

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

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


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

不安全的
块之外,因为堆栈由CLR管理。

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

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

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

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


那么,至少10堆。

Lukas你看到Jakub Konecki的答案了吗?它与“…因此您无法访问其他线程堆栈”有何关联??为什么会存在“锁定”机制?你当然可以从一个线程访问另一个线程的堆栈,不过最好避免这种直接通信,并使用可以从两个线程访问的对象队列。吹毛求疵:进程具有(正常)托管堆,一个大对象堆,你可以把非托管堆看作是3。@ MartinJames——访问另一个线程栈当然不是件容易的事。在本机代码中可能有指针。@james当然,在.Net中是复杂的。默认情况下,该选项处于禁用状态。与“联合国”