任何可以提高运行效率降低运行运行开销的行为(网络、数据传输方式、框架)
内存管理
内存:由可读写单元组成,表示一片可操作的内存空间
管理:人为的去操作一片空间的申请、使用和释放
内存管理:开发者主动去申请空间、使用空间、释放空间
js中的内存管理
// 申请空间
let obj = {}
// 使用空间
obj.name = 'lg'
// 释放空间
obj = null
javascript中的垃圾
- js中内存管理是自动的
- 对象不在被引用时是垃圾
- 对象不能从跟上访问时是垃圾
js中的可达对象
- 可以访问到的对象就是可达对象(引用、作用域链)
- 可达的标准就是从根出发是否能够被找到
- js中的根可以被理解为全局变量对象
找到垃圾,让js引擎进行空间的释放和回收
GC算法
GC: 垃圾回收机制
- 程序中不在需要使用的对象
- 程序中不能再访问到的对象
GC算法是什么
- GC是一种机制,垃圾回收器完成的具体工作
- 工作的内容就是查找垃圾释放空间、回收空间
- 算法就是工作时查找和回收所遵循的规则
引用计数算法实现原理
核心思想: 设置引用数。判断当前引用数是否为0
引用计数器:
引用关系改变时修改引用数字
引用数字为0时立即回收
const user1 = {age: 11}
const user2 = {age: 12}
const user3 = {age: 13}
const namelist = [user1.age, user2.age, user3.age]
function fn() {
const num1 = 1
const num2 = 2
}
fn()
优点:发现垃圾时立即回收,最大限度减少程序暂停
缺点:无法回收循环引用的对象,时间开销大
function fn() {
const obj1 = {}
const obj2 = {}
obj1.name = obj2
obj2.name = obj1
return 'zxt'
}
fn()
标记清除算法
核心思想: 分标记和清除两个阶段完成
遍历所有对象找标记活动对象
遍历所有对象清除没有标记对象
回收相应空间
优点:解决循环引用不能回收问题
缺点:产生空间碎片化问题,不能最大空间使用
标记整理算法
可以看成标记清除的增强
标记阶段的操作和标记清除一致
清除阶段会先执行整理,移动对象位置
优点:减少碎片化空间
缺点:不会立即回收垃圾对象
V8
是一款主流的js执行引擎
采用即时编译
v8内存设限
垃圾回收策略
-
采用分代回收的思想
-
内存分为新生代、老生代
-
针对不同对象采用不同算法
v8常用GC算法:分代回收、空间复制、标记清除、标记整理、标记增强
回收新新生代对象
-
v8内存空间一分为二
-
小空间用于存储新生代对象(32m|16m)
-
新生代是指存活时间较短的对象
实现
- 回收过程采用复制算法+标记整理
- 新生代内存区分为两个等大小空间
- 使用空间为Form,空闲空间为To
- 活动对象存储于Form空间
- 标记整理后将活动对象拷贝至To
- Form与To交换空间完成释放
回收细节说明
- 拷贝过程中可能出现晋升
- 晋升就是将新生代对象移动至老生代
- 一轮GC还存活的新生代需要晋升
- To空间使用率超过25%
回收老生代对象
-
老生代对象放在右侧老生代区域
-
64位操作系统1.4G,32位操作系统700M
-
老生代对象就是指存活时间较长的对象
实现
- 主要采用标记清除、标记整理、增量标记算法
- 首先使用标记清除完成垃圾空间的回收
- 采用标记整理进行空间优化
细节对比
- 新生代区域垃圾回收使用空间交换时间
- 老生代区域垃圾回收不适合复制算法
标记增量如何优化垃圾回收
performance工具介绍
监控内存的几种方式
浏览器任务管理器
Timeline时序图记录
堆快照查找分离DOM
分离DOM
- 界面元素存活在dom上
- 垃圾对象时的dom节点
- 分离状态的DOM节点
为什么确定是频繁垃圾回收
-
GC工作时应用程序是停止的
-
频繁且过长的GC会导致应用假死
-
用户使用中感知应用卡顿
- Timeline中频繁的上升下降
- 任务管理器中频繁的增加减少
慎用全局变量
- 全局变量定义在全局执行上下文,是所有作用域链的顶端
- 全局执行上下文一直存在于上下文执行栈,直到程序退出
- 如果在某个局部作用域出现了同名变量则会遮蔽或污染全局
缓存全局变量
通过原型对象添加附加方法
var fn1 = function () {
this.foo = function () {
console.log(1111)
}
}
fn1 = new fn1()
var fn2 = function () {}
fn2.prototype.foo = function () {
console.log(1111)
}
fn2 = new fn2()
避开闭包陷阱
function foo () {
var el = dociment.getElementById('btn')
e.onclick = function() {
console.log(el.id)
}
el = null
}
foo()
避免属性访问方法使用
- JS不需属性的访问方法,所有属性都是外部可见的
- 使用属性访问方法只会增加一层重定义,没有访问控制力
function Person() {
this.name = 'zs'
this.age = 18
this.getAge = function () {
return this.age
}
}
const p1 = new Person()
const a = p1.getAge()
function Person() {
this.name = 'zs'
this.age = 18
}
const p2 = new Person()
const b = p2.age
FOR循环优化
var btns = dociment.querySelector('btn')
for (var i = 0; i < btns.length; i++) {
console.log(i)
}
// 优
for (var i = 0; len = btns.length; i < len ; i++) {
console.log(i)
}
最有循环算法
var arr = new Array(1, 2, 3, 4, 5)
// 优
arr.forEach(item => {
console.log(item)
})
// 二
for (var i = 0; len = arr.length; i < len ; i++) {
console.log(arr[i])
}
for (var i in arr) {
console.log(arr[i])
}
文档碎片优化节点添加
for (var i = 0; i < 10; i++) {
var oP = document.createElement('p')
oP.innerHTML = i
document.body.appendChild(oP)
}
// 优
const fragEle = document.createDocumentFragment()
for (var i = 0; i < 10; i++) {
var oP = document.createElement('p')
oP.innerHTML = i
fragEle.appendChild(oP)
}
document.body.appendChild(fragEle)
克隆优化节点操作
for (var i = 0; i < 3; i++) {
var oP = document.createElement('p')
oP.innerHTML = i
document.body.appendChild(oP)
}
// 优
var oP = document.createElement('p')
for (var i = 0; i < 3; i++) {
var newP = oldP.cloneNode(false)
oP.innerHTML = i
document.body.appendChild(oP)
}
直接量替换new Object
var a = new Array(3)
a[0] = 1
a[1] = 2
a[2] = 3
var a = [1, 2, 3]