-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【进阶1-4期】JavaScript深入之带你走进内存机制 #15
Comments
escape analyse是从java抄过来的,原意是在可能的情况下把对象分配在栈上。。[这篇日志]的意思也是如此 |
看到第一个思考题,搜了下,null和undefined的区别。之前用的时候都是判断是不是null或者是不是undefined |
第二个问题:const 应该判断的是对象的引用,给一个对象添加属性并不会改变此对象的引用。 |
请问能根据内存回收的原理编写出, 延迟回收的方法呢? 调试过渡动画的时候经常发现因为GC导致的掉帧, 如果能把GC延迟到动画结束再开始就好了 |
@deepkolos |
第三个问题:缓存、作用域未释放(闭包) 、全局变量过多、定时器未清除、事件监听未清空、DOM的引用 |
"并不保存中栈内存中" 这里似乎有语句问题,应该是”保存在“ |
本期的主题是调用堆栈,本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,文末点击查看全部文章。
如果觉得本系列不错,欢迎点赞、评论、转发,您的支持就是我坚持的最大动力。
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。
昨天文章介绍了堆和栈,小结一下:
栈
内存(不包含闭包中的变量)堆
内存今日补充一个知识点,就是闭包中的变量并不保存中栈内存中,而是保存在
堆内存
中,这也就解释了函数之后之后为什么闭包还能引用到函数内的变量。闭包
的简单定义是:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。函数 A 弹出调用栈后,函数 A 中的变量这时候是存储在堆上的,所以函数B依旧能引用到函数A中的变量。现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。
闭包的介绍点到为止,【进阶2期】 作用域闭包会详细介绍,敬请期待。
今天文章的重点是内存回收和内存泄漏。
内存回收
JavaScript有自动垃圾收集机制,垃圾收集器会每隔一段时间就执行一次释放操作,找出那些不再继续使用的值,然后释放其占用的内存。
垃圾回收算法
对垃圾回收算法来说,核心思想就是如何判断内存已经不再使用,常用垃圾回收算法有下面两种。
引用计数
引用计数算法定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需要了。
引用计数有一个致命的问题,那就是循环引用
如果两个对象相互引用,尽管他们已不再使用,但是垃圾回收器不会进行回收,最终可能会导致内存泄露。
cycle
函数执行完成之后,对象o1
和o2
实际上已经不再需要了,但根据引用计数的原则,他们之间的相互引用依然存在,因此这部分内存不会被回收。所以现代浏览器不再使用这个算法。但是IE依旧使用。
上面的写法很常见,但是上面的例子就是一个循环引用。
变量div有事件处理函数的引用,同时事件处理函数也有div的引用,因为div变量可在函数内被访问,所以循环引用就出现了。
标记清除(常用)
标记清除算法将“不再使用的对象”定义为“无法到达的对象”。即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。
无法触及的对象包含了没有引用的对象这个概念,但反之未必成立。
所以上面的例子就可以正确被垃圾回收处理了。
所以现在对于主流浏览器来说,只需要切断需要回收的对象与根部的联系。最常见的内存泄露一般都与DOM元素绑定有关:
上面代码中,
div
元素已经从DOM树中清除,但是该div
元素还绑定在email对象中,所以如果email对象存在,那么该div
元素就会一直保存在内存中。内存泄漏
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 对于不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
内存泄漏识别方法
1、浏览器方法
2、命令行方法
使用
Node
提供的process.memoryUsage
方法。判断内存泄漏,以
heapUsed
字段为准。详细的JS内存分析将在【进阶20期】性能优化详细介绍,敬请期待。
WeakMap
ES6 新出的两种数据结构:
WeakSet
和WeakMap
,表示这是弱引用,它们对于值的引用都是不计入垃圾回收机制的。先新建一个
Weakmap
实例,然后将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在WeakMap
里面。这时,WeakMap
里面对element的引用就是弱引用,不会被计入垃圾回收机制。昨日思考题解答
昨天文章留了一道思考题,群里讨论很热烈,大家应该都知道原理了,现在来简单解答下。
答案已经写上面了,这道题的关键在于
.
的优先级高于=
,所以先执行a.x
,堆内存中的{n: 1}
就会变成{n: 1, x: undefined}
,改变之后相应的b.x
也变化了,因为指向的是同一个对象。从右到左
,所以先执行a = {n: 2}
,a
的引用就被改变了,然后这个返回值又赋值给了a.x
,需要注意的是这时候a.x
是第一步中的{n: 1, x: undefined}
那个对象,其实就是b.x
,相当于b.x = {n: 2}
今日份思考题
问题一:
从内存来看 null 和 undefined 本质的区别是什么?
问题二:
ES6语法中的 const 声明一个只读的常量,那为什么下面可以修改const的值?
问题三:
哪些情况下容易产生内存泄漏?
参考
进阶系列目录
交流
进阶系列文章汇总:https://github.com/yygmind/blog,内有优质前端资料,欢迎领取,觉得不错点个star。
我是木易杨,网易高级前端工程师,跟着我每周重点攻克一个前端面试重难点。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!
The text was updated successfully, but these errors were encountered: