前端高频面试题 js中堆和栈的区别和浏览器的垃圾回收机制

栈(stack)和堆(heap)是前端编程中的两个核心概念,它们在内存管理中扮演着重要角色。栈内存是自动分配的、固定大小的内存区域,遵循FILO(先进后出)原则,类似于乒乓球盒,先放进去的最后被取出。栈内存数据结构简单且易于管理,但容量有限。

堆内存则是动态分配的、无固定大小的内存区域,用于存储对象和数组等复杂数据类型。堆内存通过键值对存储数据,如同书架存书,通过书名快速查找书籍。堆内存的容量通常比栈内存大,但操作速度稍慢。

基本类型数据(如undefined、string、boolean、null、number)直接存储于栈中,其内存大小固定且由系统自动管理,便于内存快速释放。而引用类型数据(如对象、数组、函数)在堆中分配内存,通过引用地址在栈中存储。

在数据传递时,基本类型采用值传递,而引用类型采用地址传递。基本类型传递的是其值本身,引用类型传递的是对象在堆中的地址。这样,引用类型的值实际保存的是指向堆内存中对象地址的指针。

内存分配方面,栈内存因为其线性有序的特性,分配效率较高,但容量较小;堆内存则需要新分配存储区域,且需要在栈内存中存储地址指针,因此分配效率相对较低。

垃圾回收机制在栈内存中主要针对局部变量,当变量生命周期结束,系统自动回收其占用的内存。在堆内存中,垃圾回收更为复杂,对象在被引用时被保留,只有当对象没有被任何引用指向时,系统才会回收其内存。浏览器通常采用标记清除和引述计数两种方式实现垃圾回收。

标记清除方式中,变量进入执行环境被标记“进入环境”,离开环境被标记“离开环境”,“离开环境”的变量可被回收。垃圾收集器遍历内存,移除环境中的变量和被环境变量引用的标记,最后销毁带标记值的对象,释放内存空间。

引述计数方式跟踪记录每个值的引用次数,当引用次数变为0时,说明该值无用,可被回收。但此方式可能引起循环引用问题,需要手动释放内存。优化策略包括优化数组和对象使用,避免不必要的全局变量和计时器,及时释放脱离DOM的引用,以及合理使用闭包。

总结,栈和堆在内存管理中各有特点,栈内存基于简单数据结构,效率高但容量有限;堆内存则用于复杂数据类型,容量大但效率稍低。通过合理使用基本类型和引用类型,以及优化代码以减少垃圾回收,可以有效管理内存资源。