JS性能优化——JavaScript语言的优化

拉勾大前端的学习笔记,仅作为学习记录

内容概要

  • 内存管理
  • 垃圾回收与常见GC算法
  • V8引擎的垃圾回收
  • Performance工具
  • 代码优化实例

内存管理

  • 内存: 由可读写的单元组成,表示一片可操作的空间
  • 管理:人为的去操作一片空间的申请,使用和释放
  • 内存管理:开发者主动申请空间、使用空间、释放空间
  • 管理流程:申请-使用-释放

Js中的内存管理

  • 申请内存空间
// 申请
let obj = {}
// 使用
obj.name="lg"
// 释放
obj = null

Js中的垃圾回收

什么样的内容会被当做垃圾
因为js中内存管理是自动的,所以当对象不再被引用,对象不能从根上访问到,都是垃圾

可达对象
可以通过引用、作用于链访问到的对象就是可达对象
可达的标准是从根出发是否能够被找到
js中根就可以理解为是全局变量对象

垃圾回收
找到垃圾,让js的执行引擎来进行空间的释放和回收

js中的引用和可达

待补充

GC回收机制

GC的定义和作用

  • GC就是垃圾回收机制的简写
  • GC可以找到内存中的垃圾、并释放和回收空间

GC里面的垃圾是什么

  • 程序中不再需要使用的对象
function func(){
  name = 'lg'
  console.log(`${name} is a coder`)
}
func()
  • 程序中不能再访问到的对象
function func(){
  const name = 'lg'
  console.log(`${name} is a coder`)
}
func()

GC算法是什么

  • GC是一种机制,垃圾回收器完成具体工作
  • 工作的内容就是查找垃圾,释放空间,回收空间
    • 如何查找空间 ?
    • 释放空间的时候应该怎样去释放 ?
    • 回收空间的时候要怎样进行去分配 ?
  • 算法就是工作时查找和回收所遵循的规则

常见的GC算法

  • 引用计数 : 通过一个数字判断当前对象是不是一个垃圾
  • 标记清除: 在GC工作时给活动对象加标记判断对象是否是垃圾
  • 标记整理:和标记清除类似,只不过在后续回收过程会做一些事情
  • 分代回收:在V8中会用到的回收机制
引用计数算法实现原理

核心思想:设置引用数,判断当前引用数是否为0
算法规则:当某个对象引用关系发生改变的时候,引用计数器会去修改这个对象所对应的引用数值,引用数字为0时立即回收
优点

  • 发现垃圾立即回收
  • 最大限度减少程序暂停

缺点

  • 无法回收循环引用的对象
function func(){
  const obj1 = {}
  const obj2 = {}

  obj1.name = obj2
  obj2.name = obj1  // 当垃圾回收obj1的时候会发现obj1被obj2所引用,obj1的引用计数不为0,所以GC引用计数算法下,obj1不能被回收
}
func()
  • 时间开销大
标记清除算法的实现原理

核心思想:分标记和清除两个阶段

  • 标记阶段: 遍历所有对象找到并标记活动对象(也就是可达对象)
  • 清除阶段:遍历所有对象清除没有被标记的对象,把第一阶段的标记抹掉方便GC继续下次工作
    最后,把回收的空间放到空闲列表上面,方便后面的程序直接申请空间使用

优点

  • 可以解决对象循环引用的回收操作

缺点

  • 回收空间地址不连续,空间的碎片化:由于当前我们回收的对象在地址上是不连续的,从而造成回收后他们分散在各个角落,一旦新的空间申请大于或者小于当前的空间碎片,会造成空间的浪费或空间不足
  • 不能立即回收垃圾对象
标记整理算法实现原理

标记整理可以看做是标记清除的增强
标记阶段的操作和标记清除一致
清除阶段会先执行整理,移动对象位置
优点

  • 减少碎片化空间

缺点

  • 不能立即回收垃圾对象

认识V8

  • 主流的js执行引擎
  • 采用即时编译,之前很多js引擎都需要将代码转为字节码,然后才能去执行,V8是直接将代码编译为可执行的机器码,速度上快了很多
  • V8内存设有上限,64位操作系统上限不超过1.5G,32位系统不超过800M

V8垃圾回收策略

  • 采用分代回收的思想
  • 内存分为新生代、老生代
  • 针对不同代的算法采用不同的GC算法

V8中常用的GC算法

  • 分代回收
  • 空间复制
  • 标记清除
  • 标记整理
  • 标记增量

V8如何回收新生代对象

V8内存分配
新生代对象(64位32M | 32位16M) / 老生代的对象
新生代指的是存活时间较短的对象

新生代对象的回收实现

主要用复制算法+ 标记算法,将储存新生代对象的空间分为两个等大小的空间,使用空间叫做From ,空闲空间叫做To

  • 所有的所动对象都在From里面
  • 当From存储到一定量时,触发GC操作
  • 标记整理后,把标记的活动对象copy到To
  • 然后把To Copy到From,From到To进行空间置换,达到From的空间释放
    细节说明
  • copy过程发现变量晋升,
    晋升含义: 某个对象使用空间在老生代对象中也出现过,将新生代移到老生代储存
    晋升触发时机:
  • 一轮GC后还存活的新生代需要晋升
  • To空间的使用率超过25%
老生代回收说明

老生代对象说明

  • 存在右侧的老生代区,64位1.4G,32位700M
  • 存活时间较长的对象

回收实现
标记清除,标记整理,增量标记算法

步骤

  1. 首先标记清除
  2. 当想把新生代放入老生代的时候,并且当老生代空间不足的时候,触发标记整理
  3. 最后采用增量标记进行效率优化
细节对比
  • 新声带区域垃圾回收就是使用空间换时间
  • 老生代不适合复制算法(耗时,空间占用大)

代码优化相关

  • 慎用全局变量
  • 缓存全局变量
function getBtn(){
  let btn1 = document.getElementById('btn1')
  let btn2 = document.getElementById('btn2')
}
function getBtn(){
  let doc = document
  let btn1 = doc.getElementById('btn1')
  let btn2 = doc.getElementById('btn2')
}
  • 通过原型对象增加附加方法
function F1(){
  this.foo = function (){
      console.log(111)
  }
}
f1 = new F1()

function F2(){}
F2.property.foo =  function (){
      console.log(111)
}
f2 = new F2()
  • 避开闭包陷阱
function foo(){
  let el = document.getElementById('btn')
  el.onclick = function(){
    console.log(el.id)
  }
  el = null // 在函数内部删除对dom的引用,从而避免内存泄漏
}
foo()
  • 避免属性访问方法使用
    js不需要属性的访问方法,所有属性都是外部可见的
    使用属性访问方法只会增加一层重定义,没有访问的控制力
function Person(){
  this.name = "coder"
  this.age = 18
  this.getAge = function (){
    return this.age
  }
}
const p1 = new Person()
const pAge = p1.getAge()

function Person(){
  this.name = "coder"
  this.age = 18
}
const p1 = new Person()
const pAge = p1.age
  • For循环优化
const btns = document.getElementByClass('.btn')
for(var i ; i< btns.length;i++){
  console.log(i)
}
for(var i ;len=btns.length; i< len;i++){
  console.log(i)
}
  • 采用最优循环方式
    for / forEach / for...in
var arrList = [1,2,3,4,5]
// 最优
arrList.forEach(function(item){
  console.log(item)
})
// 第二
for(var i = arrList.length; i ;i--){
  console.log(arrList[i])
}
// 第三
for(var i in arrList){
  console.log(arrList[i])
}
  • 文档碎片优化节点添加
    节点添加操作必然会有回流和重绘
for ( var i = 0; i<10; i++ ){
  var oP = document.createElement('p')
  oP.innerHTML = i
  document.body.appendChild(oP)
}

const fragEle = document.createElement('p')
for ( var i = 0; i<10; i++ ){
  var oP = document.createElement('p')
  oP.innerHTML = i
  fragEle.appendChild(oP)
}
document.body.appendChild(fragEle)
  • 克隆优化节点操作
    当新增节点的时候,可以找当前已经存在的相似节点clone后添加到界面上
for ( var i = 0; i<3; i++ ){
  var oP = document.createElement('p')
  oP.innerHTML = i
  document.body.appendChild(oP)
}

var oldP = document.getElementById('box1')
for ( var i = 0; i<3; i++ ){
  var newP = oldP.cloneNode(false)
  newP.innerHTML = i
   document.body.appendChild(newP)
}
  • 直接量替换Object操作
    当定义一些对象和数组的时候,可以直接通过new的方式 ,也可以通过字面量
const a = new Array()
a[0] = 1
a[1] = 2
a[2] = 3

const a = [1,2,3]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容