JS内存管理和闭包
某些编程语言需要我们自己手动管理内存(C、C++),有些会自动管理内存(Java、JS、Python)。
内存管理的生命周期:
- 分配申请你需要的内存
- 使用分配的内存
- 释放不需要的内存
JS对于内存的分配:
- 基本数据类型在栈空间进行分配
- 复杂数据类型的内存的分配会在堆内存中开辟一块空间,在栈内存存储指向该空间的指针
垃圾回收<Garbage Collection, GC>
当对象不再使用时,JS引擎会自动对其进行释放。如何释放的呢?主要有两种方式:①引用计数法; ②标记清除法;
引用计数法
存在问题:可能会出现循环引用循环引用
标记清除
设置一个根对象,垃圾回收器会定期从这个根开始,找到所有从根开始有引用的对象,没有引用到的对象,认为是不可用对象。至此,window对象共有两个别名:GO 和 VO
V8引擎采取的算法
为了进行更好的优化,V8引擎结合了多种算法:
-
标记清除:主要采取的方法
-
标记整理:与标记清除相似,不过在回收期间同时会将不会被删除的对象搬运到连续的内存空间,从而避免了内存碎片。
-
分代收集:
- 对象会被分为两组:新的和旧的。
- 由于很多对象被创建之后很快就会被销毁。我们将新对象放入一块内存区(新区),旧对象放入另一片内存区(旧区)。(对于新旧对象的判定其实很简单,在新内存区中,再次划分为两片区域,新创建的对象总是放入左边,经过下一轮标记清除,未被清除的对象将放入右边,再经过下一轮标记清除,依旧没有清除的对象将放入旧区。)
- 对于新对象标记清楚的检查频次高,对于新对象检查的频次低。
-
增量收集:如果有很多对象,我们需要耗费好多时间(带来延迟)去遍历并标记整个对象集。所以引擎试图将垃圾收集工作分成几部分来做,这样会用多次微小的延迟代替一次一个大的延迟。
-
闲时收集:垃圾收集器只会在CPU空闲时间内运行,以减少对代码执行的影响
事实上,为了提高内存的管理效率,对内存的划分非常详细:
- Map space:存储一些隐藏类(如果我们创建了两个相同的对象,V8引擎会自动创建一个隐藏类)
- Old pointer space:存储旧区的旧对象
- Old data space:存储旧区的旧数据
闭包
闭包解释:
- 闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。
- 闭包让开发者可以从内部函数访问外部函数的作用域。
- 在 JavaScript 中,闭包会随着函数的创建而被同时创建。
- 广义来说,JS的每个函数都会形成闭包;狭义来说,如果一个函数访问了外层作用域的遍历,那么会形成闭包
没有闭包的代码类似这样:
/*没有闭包的代码*/
var name = "why"
var age = 18
var height = 1.88
var address = "广州市"
var intro = "了解真相, 你才能获得真正自由!"
function foo(name, age, height, address, intro, num1, num2) {
var message = "Hello World"
console.log(message, name, age, height, address, intro)
function bar() {
console.log(name)
}
bar()
}
foo(name, age, height, address, intro, 20, 30)
/*有闭包的代码*/
var message = "Hello World"
function foo() {
console.log(message)
}
内存泄漏:
// function adder(num, count) {
// return num + 5
// }
// add(100, 5)
// add(55, 5)
// add(12, 5)
// add(22, 8)
// add(35, 8)
// add(7, 8)
function createAdder(count) {
function adder(num) {
return count + num
}
return adder
}
var adder5 = createAdder(5)
adder5(100)
adder5(55)
adder5(12)
var adder8 = createAdder(8)
adder8(22)
adder8(35)
adder8(7)
console.log(adder5(24))
console.log(adder8(30))
// 之后永远不会再使用adder8
// 内存泄漏: 对于那些我们永远不会再使用的对象, 但是对于GC来说, 它不知道要进行释放的对应内存会依然保留着
释放内存泄露:
function createAdder(count) {
function adder(num) {
return count + num
}
return adder
}
var adder5 = createAdder(5)
adder5(100)
adder5(55)
adder5(12)
var adder8 = createAdder(8)
adder8(22)
adder8(35)
adder8(7)
console.log(adder5(24))
console.log(adder8(30))
// 永远不会再使用adder8
// 内存泄漏: 对于那些我们永远不会再使用的对象, 但是对于GC来说, 它不知道要进行释放的对应内存会依然保留着
adder8 = null
浏览器闭包内存的优化:会销毁闭包中用不到的属性