1. 概念
栈:先进后出,由操作系统自动分配释放,存放函数的参数值、局部变量值等。其操作方式类似于数据结构中的栈;是一种运算受限的线性表;
堆:先进先出,动态分配的空间一般由程序员来分配释放,若不主动释放,程序结束由系统回收,分配方式类似于链表;是一个特定的存储区或寄存器,他的一端是固定的,另一端是浮动的。
2. 区别
堆和栈实际是操作系统对进程占用的内存空间的两种管理方式,主要有一下几种区别:
(1)管理方式不同
栈:由操作系统自动分配释放,不需要我们控制
堆:由程序员控制申请和释放,容易产生内存泄漏
(2)空间大小不同,每个进程中栈的大小要远远小于堆的大小
栈:windows64位1M,linux默认10M
堆:理论上,程序员申请的堆大小为虚拟内存的大小
(3)生长方向不同
栈:生长方向向下,内存地址由高到低,仅在表尾(栈顶)进行插入和删除操作的线性表,另一端是栈底;按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的在栈顶,需要读取数据时从栈顶开始弹出数据;
堆:生长方向向上,内存地址由低到高,
(4)分配方式不同
栈:有两种分配方式,静态分配和动态分配;
静态分配:由操作系统完成的,比如局部变量的分配
动态分配:由alloca函数进行分配,由操作系统进行释放,与堆的不同
堆:都是动态分配的,没有静态分配的堆
(5)分配效率不同
栈:由操作系统自动分配,会在硬件层级对栈提供支持;分配专门的寄存器存放栈的地址,压栈出栈都有专门的执行指令,这就决定了栈的效率比较高;
堆:由C/C++提供的库函数或运算符来完成申请和管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片,显然,堆的效率比栈要低得多;
(6)存放内容不同
栈:函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外函数时,要对当前函数执行断点进行保存,需要使栈来实现。首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者BSS段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。
堆:一般情况下,堆顶使用一个字节来存放堆的大小,而堆中具体存放的内容是由程序员来填充的;
3. 总结:
综上,可以看出堆和栈相比,由于大量的malloc()/free()或new/delete的使用,容易造成大量的内存碎片,并且可能引发用户态和核心态的切换,效率较低。
栈相比于堆,在程序中应用较为广泛,最常见的是函数的调用过程由栈来实现,函数返回地址、EBP、实参和局部变量都是采用栈的方式存放,虽然栈有众多的好处,但是由于和堆比不是那么的灵活,有的时候分配大量的空间,还是用堆。
无论是堆还是栈,在内存使用中都要防止非法越界,越界导致的非法内存访问可能会摧毁程序的堆、栈数据,轻则导致程序的运行处于不确定状态,获取不到预期结果,重则导致程序一场崩溃,这些都是我们编程时与内存打交道时应该注意的问题。
前端相关
javascript 数据分为两大类: 基本类型、引用类型
1. 基本类型:null、undefined、Number、String、Boolean,ES6还新增了一种Symbol
基本数据类型可以直接访问,他们是按照值进行分配的,存放在栈(stack)中的简单数据段,数据大小不确定,内存空间大小可以分配。
2. 引用类型:及Object,是存放在堆(heap)内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。
扩展:说到堆栈内存,自然就会涉及到闭包、浅拷贝、深拷贝,还有call、bind、apple
(1)浅拷贝和深拷贝,只有引用类型才有浅拷贝、深拷贝这么一说,基本数据类型没有
最常用的深拷贝方法:序列法和反序列法
const deepClone = (obj) => { return JSON.parse(JSON.stringify(obj)) }标签:函数,区别,前端,概念,存放,内存,拷贝,分配,操作系统 From: https://www.cnblogs.com/liql/p/16914216.html