堆栈和堆并没有你想象的那么不同!
确实,某些操作系统确实存在堆栈限制。 (其中一些也有令人讨厌的堆限制!)
但这已经不是 1985 年了。
这些天,我在运行 Linux!
我的默认 stacksize 限制为 10 MB。我的默认 heapsize 是无限的。无限制堆栈大小非常简单。 (*cough* [tcsh] unlimit stacksize *cough*. 或者 setrlimit().)
stack和heap最大的区别是:
-
stack 分配只是偏移一个指针(如果堆栈变得足够大,可能会分配新的内存页面)。 堆必须搜索其数据结构以找到合适的内存块。 (也可能分配新的内存页。)
-
stack 当前块结束时超出范围。 堆在调用 delete/free 时超出范围。
-
堆 可能会变得支离破碎。 堆栈永远不会碎片化。
在Linux下,stack和heap都是通过虚拟内存来管理的。
就分配时间而言,即使在严重碎片化的内存中进行堆搜索也无法映射到新的内存页面。 时间上的差异可以忽略不计!
根据您的操作系统,通常只有在您实际使用它们所映射的新内存页面时。(malloc() 分配期间NOT!)(这是一个懒惰的评估。)
(new 会调用构造函数,它可能会使用那些内存页...)
您可以通过在 stack 或 heap 上创建和销毁大型对象来破坏 VM 系统。系统是否可以/是否可以回收内存取决于您的操作系统/编译器。如果它没有被回收,堆可能能够重用它。 (假设在此期间它没有被另一个 malloc() 重新利用。)同样,如果堆栈没有被回收,它只会被重用。
虽然换出的页面需要换回,这将是您最大的打击。
在所有这些事情中,我最担心内存碎片!
寿命(超出范围时)始终是决定因素。
但是当您长时间运行程序时,碎片会逐渐增加内存占用。不断的交换最终要了我的命!
修改添加:
伙计,我被宠坏了!
这里有些东西没有加起来……我认为要么*我*完全离谱。或者其他人都是。或者,更有可能的是,两者兼而有之。或者,只是也许,两者都不是。
无论答案是什么,我都必须知道发生了什么!
...这会很长。忍受我...
在过去的 12 年中,我大部分时间都在 Linux 下工作。大约 10 年前,在各种风格的 Unix 下。我对计算机的看法有些偏颇。我被宠坏了!
我在 Windows 上做了一些工作,但还不够权威。可悲的是,Mac OS/Darwin 也没有……尽管 Mac OS/Darwin/BSD 已经足够接近,以至于我的一些知识得以延续。
使用 32 位指针时,地址空间不足 4 GB (2^32)。
实际上,STACK+HEAP组合起来就是usually limited to somewhere between 2-4 GB as other things need to get mapped in there.
(有共享内存、共享库、内存映射文件、您运行的可执行映像总是很好等)
在 Linux/Unix/MacOS/Darwin/BSD 下,您可以在运行时人为地将 HEAP 或 STACK 限制为您想要的任意值。但最终会有一个硬系统限制。
这是 "limit" 与 "limit -h" 的区别(在 tcsh 中)。或者(在 bash 中)“ulimit -Sa” vs “ulimit -Ha”。或者,以编程方式,struct rlimit 中的 rlim_cur 与 rlim_max。
现在我们进入有趣的部分。关于Martin York 的准则。 (谢谢Martin!很好的例子。尝试一下总是好的!。)
Martin's 大概在 Mac 上运行。 (相当新的一个。他的编译器版本比我的要新!)
当然,他的代码默认不会在他的 Mac 上运行。但如果他首先调用 "unlimit stacksize" (tcsh) 或 "ulimit -Ss unlimited" (bash),它会运行得很好。
问题的核心:
在一个古老的(过时的)Linux RH9 2.4.x 内核盒上进行测试,分配大量 STACK OR HEAP,其中一个由本身最高在 2 到 3 GB 之间。 (遗憾的是,这台机器的 RAM+SWAP 的上限略低于 3.5 GB。它是一个 32 位操作系统。这不是唯一运行的进程。我们利用我们所拥有的...... )
所以在 Linux 下,STACK 大小和 HEAP 大小真的没有限制,除了人工的......
但是:
在 Mac 上,堆栈大小的硬性限制为 65532 KB。这与事物在内存中的布局方式有关。
Normally, you think of an idealized system as having STACK at one end of the memory address space, HEAP at the other, and they build towards each other. When they meet, you are out of memory.
Mac 似乎将它们的共享系统库固定在限制两侧的固定偏移量处。您仍然可以使用“无限堆栈大小”运行 Martin York 的代码,因为他只分配了 8 MiB (但他会在 HEAP 用完之前用完 STACK。
我在 Linux 上。我不会。 Sorry kid. Here's a Nickel. Go get yourself a better OS.
There are workarounds for the Mac. But they get ugly and messy and involve tweaking the kernel or linker parameters.
从长远来看,除非 Apple 做一些真正愚蠢的事情,否则 64 位地址空间会在某个时候让整个堆栈限制变得过时。
继续碎片化:
任何时候你将一些东西推到 STACK 上,它都会被附加到最后。只要当前块退出,它就会被移除(回滚)。
因此,STACK 中没有漏洞。这都是一大块已用内存。最后可能只有一点未使用的空间,都可以重复使用。
相反,当 HEAP 被分配和释放时,你最终会出现未使用的内存漏洞。随着时间的推移,这些会逐渐导致内存占用增加。不是我们通常所说的核心泄漏,但结果是相似的。
内存碎片不是避免HEAP存储的原因。这只是您在编码时需要注意的事情。
这会带来 SWAP THRASHING:
- 如果您已经分配/使用了大量堆。
- 如果你有很多零散的洞。
- 如果您有大量的小分配。
然后,您可以得到大量变量,所有变量都在代码的一个小的局部区域内使用,这些变量分散在大量的虚拟内存页面中。 (就像你在这个 2k 页面上使用 4 个字节,在那个 2k 页面上使用 8 个字节,等等很多页面......)
所有这些都意味着您的程序需要换入大量页面才能运行。或者它会不断地交换页面。 (我们称之为颠簸。)
另一方面,如果在 STACK 上进行了这些小分配,它们都将位于连续的内存段中。需要加载的 VM 内存页面更少。 (4+8+...
旁注:我提请注意这一点的原因源于我认识的一位电气工程师,他坚持将所有阵列分配在 HEAP 上。我们正在为图形做矩阵数学。一个 *LOT* 的 3 或 4 个元素数组。单独管理新/删除是一场噩梦。甚至在课堂上被抽象出来也会引起悲伤!
下一个话题。线程:
是的,默认情况下,线程仅限于非常小的堆栈。
您可以使用 pthread_attr_setstacksize() 更改它。虽然取决于您的线程实现,但如果多个线程共享相同的 32 位地址空间,单个线程堆栈将是一个问题!没有那么多空间!同样,过渡到 64 位地址空间 (OS) 会有所帮助。
pthread_t threadData;
pthread_attr_t threadAttributes;
pthread_attr_init( & threadAttributes );
ASSERT_IS( 0, pthread_attr_setdetachstate( & threadAttributes,
PTHREAD_CREATE_DETACHED ) );
ASSERT_IS( 0, pthread_attr_setstacksize ( & threadAttributes,
128 * 1024 * 1024 ) );
ASSERT_IS( 0, pthread_create ( & threadData,
& threadAttributes,
& runthread,
NULL ) );
关于Martin York 的 Stack Frames:
也许你和我在想不同的事情?
当我想到一个堆栈框架时,我想到了一个调用堆栈。每个函数或方法都有自己的堆栈帧,由返回地址、参数和本地数据组成。
我从未见过堆栈框架的大小有任何限制。 STACK 整体上存在限制,但这是所有 堆栈帧 的组合。
There's a nice diagram and discussion of stack frames over on Wiki.
最后一点:
在 Linux/Unix/MacOS/Darwin/BSD 下,可以通过编程方式更改最大 STACK 大小限制以及 limit(tcsh) 或 ulimit(bash):
struct rlimit limits;
limits.rlim_cur = RLIM_INFINITY;
limits.rlim_max = RLIM_INFINITY;
ASSERT_IS( 0, setrlimit( RLIMIT_STACK, & limits ) );
只是不要尝试在 Mac 上将其设置为 INFINITY...并在尝试使用它之前对其进行更改。 ;-)
延伸阅读: