1、为何需要垃圾回收
在V8引擎逐行执行JavaScript代码的过程中,当遇到函数的情况时,会为其创建一个函数执行上下文(Context)环境并添加到调用堆栈的栈顶,函数的作用域(handleScope)中包含了该函数中声明的所有变量,当该函数执行完毕后,对应的执行上下文从栈顶弹出,函数的作用域会随之销毁,其包含的所有变量也会统一释放并被自动回收。试想如果在这个作用域被销毁的过程中,其中的变量不被回收,即持久占用内存,那么必然会导致内存暴增,从而引发内存泄漏导致程序的性能直线下降甚至崩溃,因此内存在使用完毕之后理当归还给操作系统以保证内存的重复利用。但是JavaScript作为一门高级编程语言,并不像C语言或C++语言中需要手动地申请分配和释放内存,V8引擎已经帮我们自动进行了内存的分配和管理,好让我们有更多的精力去专注于业务层面的复杂逻辑,这对于我们前端开发人员来说是一项福利,但是随之带来的问题也是显而易见的,那就是由于不用去手动管理内存,导致写代码的过程中不够严谨从而容易引发内存泄漏。
2.v8数据存放
在程序运行时,存放数据地方主要分为堆内存和栈内存。
-
栈内存:因为使用的栈结构设计,所以叫栈内存,是一块连续的内存空间,处存储简单的数据,如基本类型(number、boolean、string)的值,复杂类型的堆内存指针也会存储在此,它是一个key、value的储存类型。栈内存的垃圾回收由系统自动直接管理,进行分配以及释放。
- 堆内存:是一块不连续的内存空间,效率不如栈,可以存储栈内存的所有能储存的数据并且存储object数据。需要v8引擎主动去进行垃圾回收,这篇文章讲解的也是堆内存的垃圾回收(栈内存已经是系统自动管理了)。
3.V8引擎垃圾管理的内存结构
- 新生代(new_space):大多数的对象开始都会被分配在这里,这个区域相对较小但是垃圾回收特别频繁,该区域被分为两半,一半用来分配内存,另一半用于在垃圾回收时将需要保留的对象复制过来。
- 老生代(old_space):新生代中的对象在存活一段时间后就会被转移到老生代内存区,相对于新生代该内存区域的垃圾回收频率较低。老生代又分为老生代指针区和老生代数据区,前者包含大多数可能存在指向其他对象的指针的对象,后者只保存原始数据对象,这些对象没有指向其他对象的指针。
- 大对象区(large_object_space):存放体积超越其他区域大小的对象,每个对象都会有自己的内存,垃圾回收不会移动大对象区。
- 代码区(code_space):代码对象,会被分配在这里,唯一拥有执行权限的内存区域。
- map区(map_space):存放Cell和Map,每个区域都是存放相同大小的元素,结构简单。
参考文献:
https://blog.csdn.net/a895865025/article/details/124528201
https://segmentfault.com/a/1190000042060046
https://juejin.cn/post/6844904016325902344#heading-1
浙公网安备 33010602011771号