【C++学习笔记】啥叫堆?啥叫栈?

通常——尤其是本帖——所说的“堆/栈”,其实是“堆/栈式内存分配”、“操作系统的堆/栈实现”、“操作系统实现的堆/栈管理器”、“语言的堆/栈实现及其管理器”等等等等一大筐概念的大杂烩。关于这种大杂烩的讨论过于高深莫测,非俺这等低水平人士所能置喙。

所以,低水平回复嘛,第一步得容俺先zhuangbility一下,把这一堆大杂烩“正交分解”,然后一个一个慢慢说清楚。


首先,啥叫“堆式内存分配”和“栈式内存分配”?各有什么优缺点?

堆式内存分配是内存管理的一种方式。这个管理方法对内存的申请、释放没有特殊要求,只要你记得申请了就要释放就好。
这种管理方法通用性好(甚至可以模拟栈^_^),但实现复杂,而且有一堆一堆的诸如内存碎片问题、内存池算法适用性和效率等等问题需要处理。

栈式内存分配则严格限制了内存的申请和分配的次序。它要求先申请的内存不能在后申请的内存归还之前归还。这就导致它的用途非常单一,局限于特定的场景(函数调用)和某些特定算法。但也正因为它的单一,使得它的管理算法简单,速度快。加上加速、简化函数/过程调用设计的需要,几乎所有现代CPU都内置了push/pop之类的栈操作指令,大大加速和简化了函数调用/返回功能代码。





显然,堆式管理的内存就是堆,被栈式管理的内存自然就是栈。

那么,堆和栈都在哪里?

无处不在。

不仅无处不在,它们还彼此嵌套、改头换面、难解难分。



比如,一个进程诞生了,操作系统要做些什么?

1、使用自己的堆管理器,为它申请一块空间当作进程控制块
2、在进程控制块里,为它配备一个堆管理器,帮它管理它的3G(或更大)内存空间
3、在进程堆里,替它申请2M字节(可由ulimit改变大小)空间当作运行栈;然后把这个空间一端的地址放到CPU的栈基址寄存器里,这样就可以利用CPU的push/pop系列指令自动替他管理这个栈(相当于硬件实现的栈空间管理器)

——大多情况下,人们所提到的堆和栈,其实就是这个操作系统为进程设置的堆/栈。

然后,这个进程创建了一个线程,又发生了什么?

1、让它仍然使用进程的堆管理器
2、为它另外申请xM字节作为栈空间
3、在系统空间(这点视操作系统而定)为它申请一个线程控制块,把进程堆管理器的地址记录于其中,把栈地址记录于其中

前面有高人质问:没有栈,你怎么实现线程?
嗯,低水平回答:很明显,没有堆,线程才会无法创建;有栈没栈关线程实现鸟事。
至于栈嘛……它是为了方便函数/过程调用的。

那么,没有栈,函数/过程调用操作是否就没法进行了?

答案是:
1、慢一点点而已。注意堆是可以模拟栈的,只要把原来的push+call和pop+ret操作对替换为new/malloc+call和delete/free+ret对即可。
2、抛弃了栈的过程调用,反而可能得到腾飞的机会。
比如近年火热的函数式编程,实际就是想方设法使得函数调用无副作用;这样一来,每个函数都可以“丢”到另外一个线程里去乱序执行——这是一个特别适合并行计算的设计。



回归正题,玩个BT的:
void fun(void)
{
    char buf[4*1024*1024];    //栈上申请4M内存
    tp=thread(thread_fun(buf));        //thread_fun里面实现了一个堆管理器,用以管理buf的使用
    wait_thread(tp);
}

这个buf是堆,还是栈?在哪里是堆,在哪里是栈?



可见,特定时刻特定内存属于堆还是栈,只和该内存当前的被管理方式相关;从一个堆上得到的内存完全可以在接下来当作栈来管理;从一个栈上得来的内存,同样也没人禁止你拿它当堆来提供给他人使用。

作为堆/栈的用户,你不需要关心内存来自何处、由什么方案管理——你只要知道如何申请、如何归还就好了,最多再多知道点性能数据;作为堆/栈的实现者,如何得到内存并不会妨碍你的管理机制;你的用户从你这里取得内存做了什么,你同样无权干涉。



所以,想讨论哪块内存是从堆上得来还是栈上得来,首先必须明确这里提到的堆/栈究竟是哪一级、由谁实现的。

比如,某个平台的某种C编译器上,malloc是CRT的堆内存管理器提供的接口,它要向操作系统的堆内存管理器申请内存,然后以堆的模式分配给用户使用;函数局部变量则是从进程栈里分配的内存,这个内存又是从操作系统管理的堆里面分配的。
而java虚拟机中,虚拟机自身用了宿主系统的堆和栈,但它上面跑的进程所用的堆和栈却完全和宿主系统无关;某些语言甚至可能完全没有堆/栈的概念——虽然最终它还是要使用操作系统的堆/栈。
而apache这类实现中,会先从CRT得到内存,然后把这些内存用它自己的堆管理算法管理(可以搜索下apache的内存池实现);那么这时候利用apache的接口申请内存,用的就是apache的堆。
c++则另外实现了个new/delete接口的堆管理算法(虽然其内部实现可能只是简单的调用系统堆分配算法);而STL则把这个堆管理算法扩充了池管理功能——这个内存池曾经被我替换掉,因为我只需要用stl::hash_map管理我自己的海量同构简单小对象,它的这个内存池过于复杂、缓慢了(当然,这个替换仅仅存在于使用stl::hash_map管理我的小对象时,其它时候用的还是stl的池或者c++的new/delete;当然,可怕的是,当时俺们组还有个牛人,相信微软的VirtualAlloc分配的内存既不在堆上,也不在栈上,是速度最快的,所以大量使用了这个高级东东-.-)——有人能理清我这个系统的堆/栈结构吗?



很显然,不同的平台上的不同编译器下,堆/栈层次树可能是非常不同的。

这些通通都不区分,却热衷于帮C委员会定义哪个东西来自于堆、哪个东西来自于栈——不好意思,这种高水平的讨论,实在不是我等低水平人士所能理解。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值