今天小编给大家分享的是javascript有垃圾回收机制gc吗,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。
javascript中有GC(垃圾回收机制)。JavaScript是使用垃圾回收机制的语言,执行环境负责在代码执行时管理内存,会自动将垃圾对象(没有被引用的对象)从内存中销毁。
JavaScript 中的垃圾回收机制(GC)
垃圾回收相关概念
① 什么是垃圾
没有被使用(引用)的对象就是垃圾。
② 什么是垃圾回收
没有被引用的对象被销毁,内存被释放,就是垃圾回收。
C、C++ 等编程语言需要手动垃圾回收。
Java、JavaScript、PHP、Python 等语言自动垃圾回收。
JS中拥有自动的垃圾回收机制,会自动将这些垃圾对象从内存中销毁,我们不需要也不能进行垃圾回收的操作。我们需要做的只是要将不再使用的对象设置为 null 即可。
为什么需要垃圾回收
引用计数法
思路
变量只是对值进行引用
当变量引用该值时,引用次数+1
当该变量的引用被覆盖或者清除时,引用次数-1
当引用次数为0时,就可以安全地释放这块内存。
let arr = [1, 0, 1] // [1, 0, 1]这块内存被arr引用 引用次数为1
arr = [0, 1, 0] // [1, 0, 1]的内存引用次数为0被释放
// [0, 1, 0]的内存被arr引用 引用次数为1
const tmp = arr // [0, 1, 0]的内存被tmp引用 引用次数为2
循环引用问题
Netscape Navigator 3.0 采用
function Example(){
let ObjectA = new Object();
let ObjectB = new Object();
ObjectA.p = ObjectB;
ObjectB.p = ObjectA;
}
Example();
ObjectA = null;
ObjectB = null;
标记清除法
为了解决循环引用造成的内存泄漏问题,Netscape Navigator 4.0 开始采用标记清除法
到了 2008 年,IE、Firefox、Opera、Chrome 和 Safari 都在自己的 JavaScript 实现中采用标记清理(或 其变体),只是在运行垃圾回收的频率上有所差异。
思路
在变量进入执行上下文时打上“进入”标记
同时在变量离开执行上下文时也打上“离开”标记
从此以后,无法访问这个变量
在下一次垃圾回收时进行内存的释放
function Example(n){
const a = 1, b = 2, c = 3;
return n * a * b * c;
}
// 标记Example进入执行上下文
const n = 1; // 标记n进入执行上下文
Example(n); // 标记a,b,c进入执行上下文
console.log(n); // 标记a, b, c离开执行上下文,等待垃圾回收
const和let声明提升性能
V8引擎的垃圾回收
V8引擎的垃圾回收采用标记清除法与分代回收法
分为新生代和老生代
新生代
新生代垃圾回收采用Scavenge
算法
分配给常用内存和新分配的小量内存
内存大小
分区
新生代内存分为以下两区,内存各占一半
From space
To space
运行
实际运行的只有From space
To space处于空闲状态
Scavenge
算法
解决了内存散落分块的问题(不连续的内存空间)
相当于用空间换时间。
当From space内存使用将要达到上限时开始垃圾回收,将From space中的不可达对象都打上标记
将From space的未标记对象复制到To space。
然后清空From space、将其闲置,也就是转变为To space,俗称反转。
新生代 -> 老生代
老生代
老生代采用mark-sweep
标记清除和mark-compact
标记整理
通常存放较大的内存块和从新生代分配过来的内存块
内存大小
分区
存储编译后的代码
存放存储对象的映射关系
存放其他区域放不下的较大的内存,基本都超过1M
字面的老生代,存放的是新生代分配过来的内存。
Old Object Space
Large Object Space
Map Space
Code Space
回收流程
Mark-compact
垃圾回收完成之后,内存空间是不连续的。
这样容易造成无法分配较大的内存空间的问题,从而触发垃圾回收。
所以,会有Mark-compact步骤将未被回收的内存块整理为连续地内存空间。
频繁触发垃圾回收会影响引擎的性能,内存空间不足时也会优先触发Mark-compact
垃圾回收优化
内存泄露场景
全局变量
// exm1
function Example(){
exm = 'LeBron'
}
// exm2
function Example(){
this.exm = 'LeBron'
}
Example()
未清除的定时器
const timer = setInterval(() => {
//...
}, 1000)
// clearInterval(timer)
闭包
function debounce(fn, time) {
let timeout = null;
return function () {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, time);
};
}
const fn = debounce(handler, 1000); // fn引用了timeout
未清除的DOM元素引用
const element = {
// 此处引用了DOM元素
button:document.getElementById('LeBron'),
select:document.getElementById('select')
}
document.body.removeChild(document.getElementById('LeBron'))
如何检测内存泄漏
这个其实不难,浏览器原带的开发者工具Performance就可以
步骤
F12打开开发者工具
选择Performance工具栏
勾选屏幕截图和Memory
点击开始录制
一段时间之后结束录制
结果
堆内存会周期性地分配和释放
如果堆内存的min值在逐渐上升则存在内存泄漏
优化内存使用
1、尽量不在for循环中定义函数
// exm
const fn = (idx) => {
return idx * 2;
}
function Example(){
for(let i=0;i<1000;i++){
//const fn = (idx) => {
// return idx * 2;
// }
const res = fn(i);
}
}
2、尽量不在for循环中定义对象
function Example() {
const obj = {};
let res = "";
for (let i = 0; i < 1000; i++) {
// const obj = {
// a: i,
// b: i * 2,
// c: i * 3,
// };
obj.a = i;
obj.b = i * 2;
obj.c = i * 3;
res += JSON.stringify(obj);
}
return res
}
3、清空数组
arr = [0, 1, 2]
arr.length = 0; // 清空了数组,数组类型不变
// arr = [] // 重新申请了一块空数组对象内存
关于javascript有垃圾回收机制gc吗就分享到这里了,希望以上内容可以对大家有一定的参考价值,可以学以致用。如果喜欢本篇文章,不妨把它分享出去让更多的人看到。