线程有不同的堆吗?


114

据我所知,当操作系统创建线程时,每个线程都会获得一个不同的堆栈。我想知道每个线程是否也有与自己不同的堆吗?


是的,Windows和Linux,C库

3
真好 +1让这些基本问题不断涌现。

Answers:


128

否。所有线程共享一个公共堆。

每个线程都有一个专用堆栈,它可以快速添加和删除其中的项目。这样可以使基于堆栈的内存速度更快,但是如果您使用过多的堆栈内存(如无限递归中的情况),则会出现堆栈溢出。

由于所有线程共享同一个堆,因此必须同步对分配器/取消分配器的访问。有很多避免分配器争用的方法和库。

某些语言允许您创建专用的内存池或单个堆,您可以将其分配给单个线程。


5
通常,线程共享资源,例如内存,因此任何非braindead线程实现都将共享堆。
R. Martinho Fernandes,2009年

10
主要的原因每个线程都有自己的堆栈,使线程实际上可以做一些事情(如通话功能一)...
埃德蒙

3
每个线程都有一个单独的堆栈,但不一定是“专用”的。通常允许其他线程访问它。
2014年

you will get a stack overflow.堆栈溢出导致堆栈溢出!
John Strood

2
@crisron可以为每个线程设置一个单独的堆,但是如果您这样做而不是使用默认的共享堆,则例如线程A分配缓冲区,将其填充数据并将其传递给线程B变得很困难,并让线程B使用数据然后释放缓冲区(因为线程B无法访问线程A的堆,线程B无法释放缓冲区;最好的线程B可以做到的是将缓冲区传递回线程A再次让线程A释放它)。
杰里米·弗里斯纳

9

默认情况下,C只有一个堆。

就是说,一些知道线程的分配器将对堆进行分区,以便每个线程都有自己的区域来分配。这个想法是,这应该使堆伸缩性更好。

此类堆的一个例子是Hoard


默认情况下,C和C ++没有多个线程。2003 c ++规范至少在其虚拟机设计中不保留线程余量,因此c ++中的线程是实现定义的。
克里斯·贝克

即使不同的线程从堆上分配不同的区域,它们仍然可以看到另一个线程分配的数据,因此这些线程仍共享同一堆。
肯·布鲁姆

1
更新:从C ++ 11开始,不再定义线程实现。
迈克尔·多斯特

5

取决于操作系统。Windows和unices上的标准c运行时在线程之间使用共享堆。这意味着锁定每个malloc / free。

例如,在Symbian上,每个线程都有自己的堆,尽管线程可以共享指向任何堆中分配的数据的指针。在我看来,Symbian的设计更好,因为它不仅消除了在alloc / free期间锁定的需求,而且鼓励对线程之间的数据所有权进行明确的规范。同样在这种情况下,当线程死亡时,它会带走它所分配的所有对象-即它不能泄漏已分配的对象,这是在内存受限的移动设备中拥有的重要属性。

Erlang也遵循类似的设计,其中“进程”充当垃圾收集的单元。除了二进制引用引用计数的二进制Blob(我认为)外,所有数据都通过复制在进程之间进行通信。


3

每个线程都有自己的堆栈和调用堆栈。

每个线程共享相同的堆。


3

这取决于您说“堆”时的确切含义。

所有线程共享地址空间,因此可以从所有线程访问分配堆的对象。从技术上讲,堆栈在这种意义上也是共享的,即没有什么可以阻止您访问其他线程的堆栈(尽管这样做几乎是没有任何意义的)。

另一方面,存在用于分配内存的堆结构。在那里完成了所有用于堆内存分配的簿记工作。这些结构经过精心组织,以最大程度地减少线程之间的争用-因此某些线程可能共享堆结构(一个竞技场),而某些线程可能使用不同的竞技场。
有关详细信息,请参见以下线程:malloc如何在多线程环境中工作?


1

通常,线程共享堆和其他资源,但是有类似线程的结构却没有。在这些类似线程的构造中,有Erlang的轻量级进程和UNIX的完整进程(通过调用来创建fork())。您可能还正在处理多计算机并发,在这种情况下,线程间通信选项将受到更大的限制。


我认为fork更像是创建一个新进程,该进程只是将数据复制到新的内存位置。
贾森·特鲁斯特鲁普

2
fork()可以在很多用例中使用,其中也可以使用线程。由于写时复制,在Unix系统上没有明显的成本差异。典型的用例是工作人员在其余服务中是自主的(例如Web服务器)。另一种可能性是通过stdin / out与主线程/程序进行通信。fork()在Unix上很强大,而Windows等其他平台更喜欢线程化。主要原因可能是使用fork()更加简单和安全,而Unix具有这种简单性。例如,请参阅apache网络服务器,它向线程的转换缓慢。
ypnos

1

一般而言,所有线程都使用相同的地址空间,因此通常只有一个堆。

但是,它可能会更复杂。您可能正在寻找线程本地存储(TLS),但它仅存储单个值。

Windows特定:TLS空间可以使用TlsAlloc分配,并可以使用TlsFree释放(在此处概述)。同样,它不是堆,而只是DWORD。

奇怪的是,Windows 每个进程支持多个。可以将堆的句柄存储在TLS中。然后,您将获得类似“线程本地堆”的信息。但是,只是其他线程不知道该句柄,它们仍然可以使用指针访问其内存,因为它仍然是相同的地址空间。

编辑:某些内存分配器(特别是FreeBSD上的jemalloc)使用TLS来为线程分配“竞技场”。这样做是通过减少同步开销来优化多个内核的分配。


>“奇怪的是,Windows每个进程支持多个堆。”,这一点都不奇怪,可以为不同类型的分配使用不同的堆,只是增加了更多的灵活性。当然,您始终可以使用VirtualAlloc,并根据需要构建自己的堆。

1

在FreeRTOS操作系统上,任务(线程)共享同一堆,但每个任务都有自己的堆栈。在处理低功耗,低RAM架构时,这非常方便,因为可以由多个线程访问/共享相同的内存池,但这带有一个小陷阱,因此开发人员需要牢记一种用于同步malloc的机制并且需要free,这就是为什么在堆上分配或释放内存时必须使用某种类型的进程同步/锁定的原因,例如信号量或互斥量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.