00:00
堆和战傻傻分不清,面试官最爱问这个,今天我用一个比喻让你彻底搞懂,保证你再也不会搞混。我也给大家准备了嵌入式自学路线,硬核实战项目,大场面是真题、简历模板等技术干货,有需要的同学66、领取你就把内存想象成一栋大楼,这样就像是这栋楼里的电梯,先进后出对吧?你第一个进电梯,肯定最后一个出来。那堆呢,就像是大楼里的仓库,你想在哪放东西就在哪放,想什么收候取就什么收候取,自由的很。先说站,当你调用一个函数的时候,系统会在站上分配一块空间,存放这个函数的局部变量、参数、返回地址这些东西,函数执行完了,这块空间立刻就被回收了,自动的你完全不用管。比如你写了个函数,里面定义了int a=10这个变量,A就在站上,函数一结束A就没了,连个招呼都不打。站的特点就是快,为什么快?因为它就是简单的移动一个指针,分配内存的时候站指针。
01:00
往下移一点,释放内存的时候,站指帧往上移一点,就这么简单,没有任何复杂的管理逻辑,而且站上的数据都是连续存放的,CPU缓存命中率特别高,访问速度贼快。但是占有个致命的缺点,空间小。一般操作系统给每个线程分配的占空间也就几兆b Linux上默认是8兆B。你要是在函数里定义一个超大的数组,比如int r了100万,那恭喜你,占役出了程序直接崩溃。这就是为什么我们说占适合存放生命周期短、占用空间小的数据。再看堆,堆就完全不一样了,当你用malock或者new申请内存的时候,系统会在堆上给你分配一块空间,这块空间是你自己管理的,你不释放它就一直在那站着。比如你写了int新p equals new int1000这个数组就在堆上,只要你不调用delete,它就永远不会被回收。堆的空间比占大多了,一般能用到几GB甚至更多,取决于你的系统内存,而且堆上的内存生命周期完全由你控制。
02:00
指你可以在一个函数里申请,在另一个函数里使用,在第三个函数里释放。这个灵活性是站完全做不到的,但灵活是要付出代价的。堆1的分配和释放比占慢得多,因为操作系统要维护一个复杂的数据结构来管理堆内存记录哪些块是空闲的,哪些块是被占用的。你申请内存的时候,系统要去这个结构里找一块合适大小的空闲块给你,释放的时候还要考虑内存碎片的问题,可能要做合并操作。这一套流程下来,开销比占大多了。更要命的是对内存你得自己管理,忘了释放就内存泄露,释放了还用就爷指针,释放两次就double free. 这三个问题不知道坑死了多少程序员。这也是为什么现代语言像Java Python都引入了垃圾回收机制,就是为了帮你自动管理堆内存。咱们来看个实际的例子,假设你要写一个函数处理字符串,如果字符串很短,比如几十个字符,你可以在站上定义char buffer64,快速高效。
03:00
函数结束自动回收。但如果字符串长度不确定,可能很大,那你就得用堆,Chasing buffer equals melo size用完了记得free还有个关键区别是生长方向。在大多数系统上,站是从高地子往低地质子生长的,就像你往下挖坑,而堆是从低地子往高地质生长的,就像你往上盖楼,它们俩从内存的两端相向生长,中间留住空间给其他用途。如果站和堆碰到一起了,那说明你的内存真的用完了。最后教你一个判断技巧,看变量的生命周期。如果一个数据的生命周期和函数调用绑定,函数结束就不需要了,那就放站上,如果数据需要在函数之间传递或者生命周期不确定,那就放堆上。看数据大小,小数据放站,大数据或者大小不确定的数据放堆,记住这个口诀,站快但小,自动管理,堆大但慢,手动管理。站是临时工,来了就走,堆是长期工,你不辞退,它就一直干。理解了这个本质。
04:00
你再去看C加加的智能指针、rust的所有权机制,就会发现他们都是在解决对内存管理的问题。好了,今天的分享就到这里,如果觉得有用,记得点赞关注,咱们下期见。
我来说两句