浏览器工作原理与实践
李兵
前盛大创新院高级研究员
立即订阅
6167 人已学习
课程目录
已完结 42 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 参透了浏览器的工作原理,你就能解决80%的前端难题
免费
宏观视角下的浏览器 (6讲)
01 | Chrome架构:仅仅打开了1个页面,为什么有4个进程?
02 | TCP协议:如何保证页面文件能被完整送达浏览器?
03 | HTTP请求流程:为什么很多站点第二次打开速度会很快?
04 | 导航流程:从输入URL到页面展示,这中间发生了什么?
05 | 渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?
06 | 渲染流程(下):HTML、CSS和JavaScript,是如何变成页面的?
浏览器中的JavaScript执行机制 (5讲)
07 | 变量提升:JavaScript代码是按顺序执行的吗?
08 | 调用栈:为什么JavaScript代码会出现栈溢出?
09 | 块级作用域:var缺陷以及为什么要引入let和const?
10 | 作用域链和闭包 :代码中出现相同的变量,JavaScript引擎是如何选择的?
11 | this:从JavaScript执行上下文的视角讲清楚this
V8工作原理 (3讲)
12 | 栈空间和堆空间:数据是如何存储的?
13 | 垃圾回收:垃圾数据是如何自动回收的?
14 | 编译器和解释器:V8是如何执行一段JavaScript代码的?
浏览器中的页面循环系统 (6讲)
15 | 消息队列和事件循环:页面是怎么“活”起来的?
16 | WebAPI:setTimeout是如何实现的?
17 | WebAPI:XMLHttpRequest是怎么实现的?
18 | 宏任务和微任务:不是所有任务都是一个待遇
19 | Promise:使用Promise,告别回调函数
20 | async/await:使用同步的方式去写异步代码
浏览器中的页面 (8讲)
21 | Chrome开发者工具:利用网络面板做性能分析
22 | DOM树:JavaScript是如何影响DOM树构建的?
23 | 渲染流水线:CSS如何影响首次加载时的白屏时间?
24 | 分层和合成机制:为什么CSS动画比JavaScript高效?
25 | 页面性能:如何系统地优化页面?
26 | 虚拟DOM:虚拟DOM和实际的DOM有何不同?
27 | 渐进式网页应用(PWA):它究竟解决了Web应用的哪些问题?
28 | WebComponent:像搭积木一样构建Web应用
浏览器中的网络 (3讲)
29 | HTTP/1:HTTP性能优化
30|HTTP/2:如何提升网络速度?
31|HTTP/3:甩掉TCP、TLS 的包袱,构建高效网络
浏览器安全 (5讲)
32 | 同源策略:为什么XMLHttpRequest不能跨域请求资源?
33 | 跨站脚本攻击(XSS):为什么Cookie中有HttpOnly属性?
34 | CSRF攻击:陌生链接不要随便点
35 | 安全沙箱:页面和系统之间的隔离墙
36 | HTTPS:让数据传输更安全
结束语 (1讲)
结束语 | 大道至简
课外加餐 (4讲)
加餐一|浏览上下文组:如何计算Chrome中渲染进程的个数?
加餐二|任务调度:有了setTimeOut,为什么还要使用rAF?
加餐三|加载阶段性能:使用Audits来优化Web性能
加餐四|页面性能工具:如何使用Performance?
浏览器工作原理与实践
登录|注册

13 | 垃圾回收:垃圾数据是如何自动回收的?

李兵 2019-09-03
上一篇文章中,我们提到了 JavaScript 中的数据是如何存储的,并通过例子分析了原始数据类型是存储在栈空间中的,引用类型的数据是存储在堆空间中的。通过这种分配方式,我们解决了数据的内存分配的问题。
不过有些数据被使用之后,可能就不再需要了,我们把这种数据称为垃圾数据。如果这些垃圾数据一直保存在内存中,那么内存会越用越多,所以我们需要对这些垃圾数据进行回收,以释放有限的内存空间

不同语言的垃圾回收策略

通常情况下,垃圾数据回收分为手动回收自动回收两种策略。
如 C/C++ 就是使用手动回收策略,何时分配内存、何时销毁内存都是由代码控制的,你可以参考下面这段 C 代码:
//在堆中分配内存
char* p = (char*)malloc(2048); //在堆空间中分配2048字节的空间,并将分配后的引用地址保存到p中
//使用p指向的内存
{
//....
}
//使用结束后,销毁这段内存
free(p);
p = NULL
从上面这段 C 代码可以看出来,要使用堆中的一块空间,我们需要先调用 mallco 函数分配内存,然后再使用;当不再需要这块数据的时候,就要手动调用 free 函数来释放内存。如果这段数据已经不再需要了,但是又没有主动调用 free 函数来销毁,那么这种情况就被称为内存泄漏
另外一种使用的是自动垃圾回收的策略,如 JavaScript、Java、Python 等语言,产生的垃圾数据是由垃圾回收器来释放的,并不需要手动通过代码来释放。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《浏览器工作原理与实践》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(30)

  • mfist
    1. 如何判断内存泄漏的?一般是感官上的长时间运行页面卡顿,猜可能会有内存泄漏。通过DynaTrace(IE)profiles等工具一段时间收集数据,观察对象的使用情况。然后判断是否存在内存泄漏。修改后验证
    2. 工作中避免内存泄漏方法:确定不使用的临时变量置为null,当前es6普及场景下少使用闭包也是一种方法。

    今日总结
    垃圾回收策略一般分为手动回收和自动回收,java python JavaScript等高级预言为了减轻程序员负担和出错概率采用了自动回收策略。JavaScript的原始类型数据和引用数据是分别存储在栈和椎中的,由于栈和堆分配空间大小差异,垃圾回收方式也不一样。栈中分配空间通过ESP的向下移动销毁保存在栈中数据;堆中垃圾回收主要通过副垃圾回收器(新生代)和主垃圾回收器(老生代)负责的,副垃圾回收器采用scavenge算法将区域分为对象区域和空闲区域,通过两个区域的反转让新生代区域无限使用下去。主垃圾回收器采用Mark-Sweep(Mark-Compact Incremental Marking解决不同场景下问题的算法改进)算法进行空间回收的。无论是主副垃圾回收器的策略都是标记-清除-整理三个大的步骤。另外还有新生代的晋升策略(两次未清除的),大对象直接分配在老生代。

    作者回复: 总结很好,还可以通过Chrome开发者工具中的Performance来观察。

    2019-09-03
    16
  • 忘忧草的约定
    老师请问:经过内存整理之后活动对象在堆中的内存地址就变化了,主线程还处于垃圾回收阶段,此时内存变化是如何更新到相应执行上下文中的呢
    2019-09-04
    4
    7
  • 一步
    对于新生代,副垃圾回收器是怎么进行标记的,文章也就一句话带过了,是和老生代标记算法一样吗?从一组跟元素开始,然后开始遍历的

    作者回复: 新生区和老生区标记过程是同一个过程,之后新生代把存活的数据移动到空闲区,老生代把死去的对象加到空闲列表中。

    2019-09-03
    5
  • 一步
    对于栈中的垃圾回收,是通过移动 ESP 指针来实现的,是不需要通过V8的垃圾回收机制的吗?

    作者回复: 是的 栈中的过期数据直接通过esp给抹掉,效率非常高。

    2019-09-03
    5
  • YBB
    有个问题想请教下,副回收器的触发频率会高于主回收器吗?还是两者是同步触发的?

    作者回复: 会的,副垃圾回收器执行速度快,而且容易满,所以回收频率会比主垃圾回收器高。

    2019-09-05
    4
  • tick
    标记的过程具体是什么样的呢?我理解老师讲的是,一个指针指向堆里,每次移动一块内存,一个指针遍历栈中,然后看栈中是否引用这块堆中的内存,但感觉这样效率很低

    作者回复: 比如全局window对象看成是一个树状结构,垃圾回收时,V8会先遍历这颗树,能遍历到的元素说明还存活的,标记为活动对象!没有被标记到的说明已经没有被引用了。

    同时V8还维护了一个空闲列表,也就是没有被使用的空闲空间列表,垃圾清理过程就是把没有标记的添加到空闲列表中!

    这样就完成了“标记-清除”操作

    2019-09-03
    4
  • 郝仁杰
    trim之后,数据在堆上的地址发生变化,v8是如何更新对应栈上的引用的

    作者回复: JavaScript中的原始字符串是不可变的(immutable),也就是说,一旦一个字符串创建了,它在内存中的值就不可能改变,这和其他语言是有区别的。

    所以当你调用trim方法后,v8引擎返回给你的是一个新字符串,并不是之前的字符串了。

    2019-09-03
    3
    4
  • ytd
    做了这么长时间的前端开发,第一次关注内存泄漏的问题,以后得多关注这方面了。通过chrome的Perfomance面板记录页面的活动,然后在页面上进行各种交互操作,过一段时间后(时间越长越好),停止记录,生成统计数据,然后看timeline下部的内存变化趋势图,如果是有规律的周期平稳变化,则不存在内存泄漏,如果整体趋势上涨则说明存在内存泄漏。另外,想问下老师,这个内存变化趋势只是js堆内存的变化吗?因为我发现在统计图表下部分了几类:JS Heap、Documents、Nodes、Listeners、GPU Memory,JS Heap是占用最多的,其次是Nodes,再次是Listeners。
    2019-09-04
    3
  • Hurry
    使用 chrome 的 Performance 面板,观察内存变化 如何多次垃圾回收后,整体趋势是向上,就存在内部泄漏的可能!

    作者回复: 这是一个很好的方法

    2019-09-03
    2
  • Lx
    我想问下,标记清除和标记整理是两个同等级的算法策略吗?目前v8使用的是两者结合,还是只有一种?

    作者回复: 标记清除和标记整理可以看成是垃圾回收的两个阶段吧,v8在实现垃圾回收过程中,两种算法都用上了。

    2019-09-03
    1
    2
  • 芒果
    大道至简,看完了浏览器的垃圾回收,让我联想到了jvm的垃圾回收,发现2者思想上基本都差不多。

    作者回复: 现代虚拟机都是抄来抄去的

    2019-11-06
    1
  • 钟钟
    增量标记的时候,比如我标记了一个对象是没有引用的,可以清除,然后又执行一段代码,重新引用了这个对象,这个是可能的吧,这个情况怎么处理呢

    作者回复: 没有引用的对象是不会重新被使用的了,能使用的都是能查到的

    2019-09-07
    1
  • 于你
    老师,我最近听了一门课,那个老师说现代的浏览器用闭包不会造成内存泄漏,因为垃圾回收是用的标记清除

    作者回复: 对,没有被引用的闭包会被自动回收,不过如果没用的闭包还保存在全局变量中,依然会内存泄漏!

    2019-09-07
    2
    1
  • 誒這麽巧我也喜歡妳
    增量标记会受到中间穿插的js应用逻辑影响么?会造成标记结果不全或者错误么?

    作者回复: 不全没关系,新产生的垃圾下次再回收,分配内存使用空闲列表里面的。

    2019-09-06
    1
  • 张峰
    v8 新生代 没有eden,s0,s1的划分吗

    作者回复: v8虽然借鉴了Java的垃圾回收器,但是v8新生代只有两个区,幸存对象会被打上标记!

    2019-09-03
    1
  • 江霖
    老师我有个问题,副垃圾回收器的回收机制是对象区域满的时候,那么主垃圾回收器呢?
    代码空间占用的内存什么时间回收呢
    2019-12-11
  • Juntíng
    可以针对页面进行压测,然后结合 chrome 的开发工具的 性能工具和内存工具 针对压测片段来分析堆栈使用情况
    2019-12-09
  • 屈悦微
    这篇文章写得很有深度,反复看了几遍,收获颇多,但是仍有一个问题,望作者百忙之中能解答
     在本篇中作者介绍了的垃圾回收机制是,标记对象的机制
     但在《javascript高级程序设计》中还介绍了引用计数的机制
    我产生以下两个疑问
    1.v8有没有使用引用计数的机制?
    2.如果有,何时使用引用计数,何时使用标记对象?

    作者回复: 引用计数有问题,会导致内存泄漏,所以现在流行的垃圾回收器都没有采用引用计数的方式!

    2019-12-06
  • -_-_aaa
    除了‘标记清除法‘还有‘引用计数法‘,那过时的‘引用计数法‘也是发生在老分区吗?
    2019-12-04
  • 噶水库
    李老师:我对老生代内存整理哪里有个疑问,内存整理是将分散对数据往一端移动,清除掉碎片内存。那如果之前有一块内存存储的数组,后面我又对数组进行添加操作。那我新增的数据是如何存储呢?(整理之后当前数组内存与其他紧挨着了。。。)
    2019-10-29
收起评论
30
返回
顶部