2021/01/30 - JavaScript内存机制
lxinr opened this issue · 0 comments
lxinr commented
javascript的内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中), 其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池
栈
栈结构遵循后进先出(LIFO),其中基本类型是保存在栈中,因为这些类型在内存中分别占有固定大小的空间,通过按值来访问
var arr = [1, 2, 3]
arr.push(4)
console.log(arr.pop()) // 4 后进先出堆
堆数据结构是一种树状结构。它的存取数据的方式与书架和书非常相似。我们只需要知道书的名字就可以直接取出书了,并不需要把上面的书取出来。JSON格式的数据中,我们存储的key-value可以是无序的,因为顺序的不同并不影响我们的使用,我们只需要关心书的名字,其中引用类型保存在堆内存中,因为这种值的大小不固定,因此不能把它们保存到栈内存中,但对象引用是放在栈内存中的,当查询引用类型的变量时, 先从栈中读取内存地址, 然后再通过地址找到堆中的值
内存回收
JavaScript有自动垃圾收集机制,垃圾收集器会每隔一段时间就执行一次释放操作,找出那些不再继续使用的值,然后释放其占用的内存
- 局部变量和全局变量的销毁
- 局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收
- 全局变量什么时候需要自动释放内存空间则很难判断,所以在开发中尽量避免使用全局变量
- 以Google的V8引擎为例,V8引擎中所有的JS对象都是通过堆来进行内存分配的
- 初始分配:当声明变量并赋值时,V8引擎就会在堆内存中分配给这个变量
- 继续申请:当已申请的内存不足以存储这个变量时,V8引擎就会继续申请内存,直到堆的大小达到了V8引擎的内存上限为止
- V8引擎对堆内存中的JS对象进行分代管理
- 新生代:存活周期较短的JS对象,如临时变量、字符串等
- 老生代:经过多次垃圾回收仍然存活,存活周期较长的对象,如主控制器、服务器对象等
垃圾回收算法
1、引用计数(现代浏览器不再使用)
引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需要了,
但假如有两个对象相互引用,尽管他们已不再使用,但是垃圾回收器不会进行回收,最终可能会导致内存泄露
2、标记清除
标记清除算法将“不再使用的对象”定义为无法到达的对象。即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收
内存泄漏
对于不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
识别方法
1、浏览器方法
- 打开开发者工具,选择 Memory
- 在右侧的Select profiling type字段里面勾选 timeline
- 点击左上角的录制按钮。
- 在页面上进行各种操作,模拟用户的使用情况。
- 一段时间后,点击左上角的 stop 按钮,面板上就会显示这段时间的内存占用情况