js内存泄漏如何检测?场景有哪些
垃圾回收
什么是垃圾回收?
function fun1() {
const a = 'cc'
console.log(a)
const obj = { x: 100 }
console.log(obj)
}
fun1()//垃圾回收:执行完了,a obj就会被回收
function fun2() {
const obj = { x: 100 }
window.obj = obj//回收不了,符合用户预期
}
fun2()
function getDataFns() {
const data = {}//闭包
return {
get(key) {
return data[key]
},
set(key, val) {
data[key] = val
}
}
}
垃圾回收的算法:
- 引用计数(早期算法)
看看对象的引用数,为0就回收
//对象被a引用
let a = {x:100}
let a1 = a
a = 10
a1 = null//此时,{x:100}引用数为0,被回收
//引用计数缺点,循环引用,不能清除
function fun3(){
const obj1 = {}
const obj2 = {}
obj1.a = obj2
obj2.a = obj1
}
fun3()
- 标记清除(现代)
定期遍历JS根window所有的子属性,得到保留,没有清除
闭包是内存泄漏吗?
其实闭包是不可以被垃圾回收
内存泄漏是非预期的情况
闭包是符合预期的,不是内存泄漏
检测工具
使用performance检测内存
内存泄漏的场景(Vue为例)
- 被全局变量、函数引用,组件销毁时未清除
- 被全局事件、定时器引用,组件销毁时未清除
- 被自定义事件引用,组件销毁时未清除
WeakMap WeakSet(点击有更详细的解释)
-
weakSet里面的对象是弱引用
在weakSet里引用了对象,垃圾回收机制不会考虑该对象的引用,若外面已经没有该对象的引用了,则垃圾回收机制会回收该对象,weakSet内部引用的该对象也会自动消失
所以weakSet内部的对象不适合被引用,也不允许遍历,因为有可能遍历的过程,内部的对象被垃圾回收机制回收了
用处:
1、在weakSet中存储一些临时的对象,当外部该对象的引用消失了,weakSet内部该对象的引用也自动消失了,不用担心内存泄漏
2、存储一些DOM节点,这样就不用DOM节点从文档中移除时,产生内存泄漏 -
weakMap键名指向的对象(注意:不是键值)不会对垃圾回收机制造成影响
在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除
浏览器和nodejs的事件循环有什么区别
单线程和异步
- js是单线程的(无论在浏览器还是在node.js)
- 浏览器中js执行和DOM渲染共用一个线程
- 异步
宏任务和微任务
- 宏任务,如setTimeout setInterval网络请求
- 微任务,如promise async/await
- 微任务是在下一轮DOM渲染之前执行,宏任务是之后执行
nodejs异步
- nodejs同样使用ES语法,也是单线程,也需要异步
- 异步任务分:宏任务+微任务
- 但是,它的宏任务和微任务,分不同的类型,有不同的优先级
nodejs宏任务类型和优先级
- Timer-setTimeout setInterval
- I/O callbacks - 处理网络、流、TCP的错误回调
- Idle、prepare-闲置状态(nodejs内部使用)
- poll轮询-执行poll中的I/O队列
- check检查-储存setImmediate回调
- close callbacks-关闭回调,如socket.on(‘colse’)
nodejs微任务类型和优先级
- promise,async/await、process.nextTick(优先级最高)
nodejs的eventloop
- 执行同步代码
- 执行微任务(process.nextTick优先级最高)
- 按顺序执行6个类型的宏任务(每个开始之前都执行当前的微任务)
浏览器和nodejs的eventloop流程基本相同
nodejs宏任务和微任务分类型、有优先级
虚拟DOM真的很快吗
vdom
- Virtual DOM,虚拟DOM
- 用js对象模拟DOM节点数据
- 最先由React推广使用
vdom并不快,js直接操作DOM才是最快的
但是‘数据驱动视图’要有合适的技术方案,不能全部DOM重建
遍历数组,for…和forEach哪个更快
const arr = []
for (let i = 0; i < 100 * 10000; i++) {
arr.push(i)
}
const length = arr.length
console.time('for')
let n1 = 0
for (let i = 0; i < arr.length; i++) {
n1++
}
console.timeEnd('for')//2.7ms
console.time('forEach')
let n2 = 0
arr.forEach(ele => { n2++ })
console.timeEnd('forEach')//3.5ms
- for更快
- forEach每次创建一个函数来调用,而for不会创建函数
- 函数需要独立的作用域,会有额外的开销
- 越低级的代码,性能会越好
nodejs如何开启进程,进程如何通讯?
进程process vs 线程thread
- 进程,OS进行资源分配和调度的最小单位,有独立空间
- 线程,OS进行运算调度的最小单位,共享进程内存空间
- js是单线程,但是可以开启多个进程执行,如WebWoker
为何需要多进程?
- 多核CPU,更适合处理多进程
- 内存较大,多个进程才能更好的利用(单进程有内存上限)
- 总之是为了压栈机器资源,更快,更省
主进程
- 开启子进程child_process.fork和cluster.fork
- 使用send和on通讯
请描述JS Bridge原理
什么是JS Bridge
- JS无法直接调用native API
- 需要通过一些特定的“格式”来调用
- 这些格式就统称为JS Bridge,例如微信的JSSDK
JS Bridge的常见实现方式
- 注册全局API(适用于简单的异步通讯,不适用于异步的情况)
- URL Scheme(自造协议,主要是通过iframe)
const sdk = {
invoke(url,data,onSuccess,onError){
const iframe = document.createElement('iframe')
iframe.style.visibility = 'hidden'
document.body.appendChild(iframe)
iframe.onload=()=>{
const content = iframe.contentWindow.document.body.innerHTML
onSuccess(JSON.parse(content))
iframe.remove()
}
iframe.onerror=()=>{
onError()
iframe.remove()
}
iframe.src= `wexin://${url}?data=${JSON.stringify(data)}`
}
}
是否了解过requestIdleCallback和requestAnimationFrame有什么区别?
由React fiber引起的关注
- 组建树转链表,可分段渲染
- 渲染时可以暂停,执行其他高优的任务,空闲时再继续渲染
- 如何判断是空闲呢?——requestIdleCallback
如图链表结构
区别:
- requestAnimationFrame每次渲染都会执行,高优
- requestIdleCallback空闲时才会执行,低优
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>requestAnimationFrame</title>
</head>
<style>
#box{
width: 100px;
height: 100px;
border: 2px solid black;
}
</style>
<body>
<p>requestAnimationFrame</p>
<button id="btn1">change</button>
<div id="box"></div>
<script>
const box = document.getElementById('box')
document.getElementById('btn1').addEventListener('click',()=>{
let curWidth =100
const maxWidth = 400
function addWidth(){
curWidth = curWidth +3
box.style.width = `${curWidth}px`
if(curWidth<maxWidth){
window.requestAnimationFrame(addWidth)//时间不用自己控制
}
}
addWidth()
})
</script>
</body>
</html>
是宏任务还是微任务?
是宏任务,在dom渲染后执行