资讯专栏INFORMATION COLUMN

前端—初级阶段5(16-20)

2i18ns / 1457人阅读

摘要:一垃圾回收与内存泄漏参考内存控制垃圾回收的垃圾回收策略主要基于分代式垃圾回收机制。内存泄漏内存泄漏的实质就是应当回收的对象因为意外没有被回收,变成了常驻在老生代中的对象。造成内存泄漏的主要原因有缓存队列消费不及时作用域未释放。

内容
1.内存泄漏与垃圾回收
2.cookie和session
3.单线程原理
4.上下左右居中的几种实现。
5.BFC和IFC模型。
一、垃圾回收与内存泄漏

参考:内存控制

1.垃圾回收

v8的垃圾回收策略主要基于分代式垃圾回收机制。按照对象的存活时间将内存的垃圾回收进行不同的分代,然后,分别对不同的分代的内存再进行高效的垃圾回收算法。在V8中,主要将内存分为新生代和老生代两代。新内存中的对象存活时间短,老内存中的对象存活时间长或常驻内存对象。

1)新生代垃圾回收算法scavenge算法

新生代中的对象主要通过scavenge算法进行垃圾回收,其主要是采用cheney算法进行具体处理。

cheney算法采用一种复制方式的垃圾回收算法,将堆内存一分为二,只有一部分空间被使用称为From空间,另一个处于闲置称为To空间。当进行分配对象的时候先在from空间分配,当进行垃圾回收时,会检查from空间中的存活对象,将这些存活对象复制到to空间中,复制完成后From和to空间角色互换,清空to空间,在垃圾回收过程中就是通过将存活对象在两个空间中进行复制。

缺点: 只能使用一半的内存

优点: 只复制存活的对象,对于生命周期短的场景存活对象只占小部分,所以时间效率高

当一个对象经过多次复制依然存活时,就会被认为是生命周期较长的对象,会被移入老生代内存中。

对于移入老生代内存有两个条件:

对象已经经过新生代内存回收机制的回收依然存活

复制到To空间的对象超过25%(为什么是25%?这个To空间接下来会成为From空间并接受内存分配,如果占比过高影响后续分配)

2)老内存垃圾回收算法Mark-Sweep & Mark-Compact

老内存中,大多是不死的老对象,用scavenge算法又费力,又占用空间,因此,采用了新的内存垃圾回收算法:Mark-Sweep & Mark-Compact。

Mark-Sweep 标记清扫

mark-sweep分为标记和清除两个阶段,mark阶段会遍历堆,然后标记处活着的对象,sweep阶段会清除没有被标记的对象。mark-sweep只清理没有标记的对象,在老内存中,死了的对象占比较少,这也是这个算法高效的原因。

mark-sweep的问题在于,每次sweep后,会存在内存碎片,这些不连续的内存碎片会占有大量空间,因此,下一次复制大对象时,将会发现空间不够,因而再次触发垃圾回收,这个回收是不必要的,也浪费了cpu。为了解决这个问题,增加了mark-compact算法。

mark-compact 标记整理和压缩
mark-compact在整理过程中,将活着的对象往一端移动,移动完成后,直接将另外一端的内存清理掉。
因为mark-compact需要移动内存,因此,垃圾回收主要使用mark-sweep,在内存不够时,才会触发一次mark-compact。

这三种算法的比较:

Incremental Marking
为了避免出现javaScript应用逻辑与垃圾回收器看到不一致的情况,垃圾回收都要将应用逻辑停下来,这种行为会造成停顿,在新生代垃圾回收过程中因为存活对象比较少,即使停顿基本影响不大。在老生代垃圾回收中,通常存活对象较多,全堆垃圾回收的标记、清除、整理影响较大。
解决办法:分批次进行,拆分成许多小步,每进行一小步就让逻辑运行一会。

v8后续还引入了lazy sweeping与incremental compaction,同时还引入了,并行标记和并行清理,进一步的利用多核性能降低每次停顿的时间。

2.内存泄漏

内存泄漏的实质就是应当回收的对象因为意外没有被回收,变成了常驻在老生代中的对象。
造成内存泄漏的主要原因有:缓存、队列消费不及时、作用域未释放。

1)缓存
慎将内存当做缓存,一旦一个对象被当做缓存来使用,那它将会常驻在老生代中,这将导致垃圾回收在进行扫描和整理时,对这些对象做无用功。
v8内存是通过垃圾回收进行处理的,没有过期策略,而真正的缓存是存在过期策略的。
缓存限制策略:将结果记录在数组中,一旦超过数量,就以先进先出的方式进行淘汰。

2)闭包
闭包是通过中间函数进行间接访问内部变量实现的一个功能,一旦变量引用这个中间函数,这个中间函数将不会释放,同时也会使原始的作用域不会得到释放,作用域中产生的内存占用也不会得到释放。除非不再有引用,才会逐步释放。

二、cookie和session

参考:构建Web应用

1.cookie

http是一个无状态的协议,现实中的业务却是需要有状态的,否则无法区分用户之间的身份。利用cookie记录浏览器与客户端之间的状态。
cookie的处理分为如下几步:

服务器向客户服务发送cookie

浏览器将cookie保存

之后每次浏览器都会将cookie发送给服务器,服务器端再进行校验

告知客户端是通过响应报文实现的,响应的cookie值在set-cookie字段中,它的格式与请求中的格式不太相同,规范中对它的定义如下:

Set-Cookie: name=value; Path=/; Expires=Sun, 23-Apr-23 09:01:35 GMT; Domain=.domain.com;

name = value是必选字段,其他为可选字段。

可选字段 说明
path 表示这个cookie影响的路径,当前访问的路径不满足该匹配时,浏览器则不发送这个cookie
Expires、Max-Age 用来告知浏览器这个cookie何时过期的,如果不设置该选项,在关闭浏览器时,会丢失掉这个cookie,如果设置过期时间,浏览器将会把cookie内容写入到磁盘中,并保存,下次打开浏览器,该cookie依旧有效。expires是一个utc格式的时间字符串,告知浏览器此cookie何时将过期,max-age则告知浏览器,此cookie多久后将过期。expires会在浏览器时间设置和服务器时间设置不一致时,存在过期偏差。因此,一般用max-age会相对准确。
HttpOnly 告知浏览器不允许通过脚本document.cookie去更改这个cookie值,也就是document.cookie不可见,但是,在http请求的过程中,依然会发送这个cookie到服务器端。
secure 当secure = true时,创建的cookie只在https连接中,被浏览器传递到服务器端进行会话验证,如果http连接,则不会传递。因此,增加了被窃听的难度。

cookie的性能影响

当cookie过多时,会导致报文头较大,由于大多数cookie不需要每次都用上,因此,除非cookie过期,否则会造成带宽的浪费。

cookie优化的建议:

减小cookie的大小,切记不要在路由根节点设置cookie,因为这将造成该路径下的全部请求都会带上这些cookie,同时,静态文件的业务不关心状态,因此,cookie在静态文件服务下,是没有用处,请不要为静态服务设置cookie。
为静态组件使用不同的域名,cookie作用于相同的路由,因此,设定不同的域名,可以防止cookie被上传。
减少dns查询,这个可以基于浏览器的dns缓存来削弱这个副作用的影响(换用额外域名需要DNS查询)。
cookie的不安全性
cookie可以在浏览器端,通过调用document.cookie来请求cookie并修改,修改之后,后续的网络请求中就会携带上修改过后的值。
例如:第三方广告或者统计脚本,将cookie和当前页面绑定,这样可以标识用户,得到用户浏览行为。

2.session

cookie存在各种问题,例如体积大、不安全,为了解决cookie的这些问题,session应运而生,session只保存在服务器端,客户端无法修改,因此,安全性和数据传递都被保护。

如何将每个客户和服务器中的数据一一对应:

基于cookie来实现用户和数据的映射

通过查询字符串来实现浏览器端和服务器端数据的对应。它的原理是检查请求的查询字符串,如果没值,会先生成新的带值的URL。

安全性
session的口令保存在浏览器(基于cookie或者查询字符串的形式都是将口令保存于浏览器),因此,会存在session口令被盗用的情况。当web应用的用户十分多,自行设计的随机算法的口令值就有理论机会命中有效的口令值。一旦口令被伪造,服务器端的数据也可能间接被利用,这里提到的session的安全,就主要指如何让这一口令更加安全。

有一种方法是将这个口令通过私钥加密进行签名,使得伪造的成本较高。客户端尽管可以伪造口令值,但是由于不知道私钥值,签名信息很难伪造。如此,我们只要在响应时将口令和签名进行对比,如果签名非法,我们将服务器端的数据立即过期即可,

将口令进行签名是一个很好的解决方案,但是如果攻击者通过某种方式获取了一个真实的口令和签名,他就能实现身份的伪造了,一种方案是将客户端的某些独有信息与口令作为原值,然后签名,这样攻击者一旦不在原始的客户端上进行访问,就会导致签名失败。这些独有信息包括用户IP和用户代理。

三、单线程原理 1.单线程

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

2.任务队列

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

主线程不断重复上面的第三步

js引擎执行异步代码而不用等待,是因有为有 消息队列和事件循环。

消息队列:消息队列是一个先进先出的队列,它里面存放着各种消息。
事件循环:事件循环是指主线程重复从消息队列中取消息、执行的过程。

3.事件循环进阶:macrotask与microtask

JS中分为两种任务类型:macrotask和microtask,在ECMAScript中,microtask称为jobs,macrotask可称为task。

宏任务(macrotask):setTimeout, setInterval, setImmediate, requestAnimationFrame,I/O, UI rendering。

微任务(microtask):process.nextTick, Promise.then catch finally, MutationObserver,Object.observe 。

在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

宏任务和微任务之间的关系:

事件循环机制进一步补充

主线程运行时会产生执行栈,栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕)

而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调

如此循环

注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件

参考

1.http://www.ruanyifeng.com/blo...
2.https://www.jianshu.com/p/f47...
3.实例

四、上下左右居中的几种实现

https://blog.csdn.net/mars200...

五、BFC和IFC模型 1.BFC

BFC(Block Formatting Context)叫做“块级格式化上下文”。
当一个元素设置了新的BFC后,就和这个元素外部的BFC没有关系了,这个元素只会去约束自己内部的子元素。

1)BFC的布局规则如下:

内部的Box会在垂直方向,一个接一个地放置。

Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠

每个盒子的左边界都要紧靠包含容器的左边界。即使存在浮动也是如此。除非这个元素自己形成了一个新的BFC。

BFC的区域不会与float box重叠。

BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

计算BFC的高度时,浮动元素也参与计算

2)如何产生新的BFC

根元素;

float不为none;

position为absolute,fixed;

display为inline-block,table-cell,table-caption,flex;

overflow不为visible;

3)实际应用

清除浮动 例子

margin折叠问题

两侧布局:左边固定,右边自适应

双飞翼布局 例子

张鑫旭:CSS深入理解流体特性和BFC特性下多栏自适应布局

2.IFC

IFC(Inline Formatting Content)叫做 行内格式化上下文。
1)规则

盒子是水平一个接一个的排列,水平的margin,内边距,边框是可以有的。

垂直方向的对齐,可能是底部对齐,顶部对齐,也可能是基线对齐(这个是默认的);ps.这里的盒子应该是指的内联元素的盒子(span,strong等)和匿名内联盒子(只有文本,没有内联元素包含,自动创建的),他们合称内联盒子,一个或者多个内联盒子组成一个行框,行框的宽度由包含块和出现的浮动决定的。

行框中的内联盒子的高度小于行框的高度时,内联盒子的垂直方向的对齐方式取决于vertical-align属性

当一个行框水平不能容纳内联盒子时,他们将会在垂直方向上产生多个行框,他们上下一个挨着一个,但是不会重叠

一般来说,行框的左边界紧挨着包含容器的左边界,行框的右边界紧挨着包含容器的右边界,(是两个边都紧挨着)。然而,浮动盒子可能存在于包含边框边界和行框边界之间;

多个内联盒子的宽度小于包含他们的行框时,他们在水平方向的分布取决于text-align属性(默认是left)

2)主要影响IFC内布局的css: 参考

font-size

line-height

height

vertical-aligin

容器的高度 height = line-height + vertical-align

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110198.html

相关文章

  • 从jvm角度看懂类初始化、方法重载、重写。

    摘要:对应的代码接下来的句是关键部分,两句分分别把刚刚创建的两个对象的引用压到栈顶。所以虽然指令的调用是相同的,但行调用方法时,此时栈顶存放的对象引用是,行则是。这,就是语言中方法重写的本质。 类初始化 在讲类的初始化之前,我们先来大概了解一下类的声明周期。如下图 类的声明周期可以分为7个阶段,但今天我们只讲初始化阶段。我们我觉得出来使用和卸载阶段外,初始化阶段是最贴近我们平时学的,也是笔试...

    tinyq 评论0 收藏0
  • 关于SVG

    摘要:以前前端时代做按钮,图标背景图用,后来主流现在各种流体结构,高分屏的需求,大行其道,无论是体积还是灵活性还是品质都远胜。 以前前端IE6时代做按钮, 图标背景图用gif, png, 后来主流png, 现在各种流体结构,高分屏的需求, svg大行其道, 无论是体积还是灵活性还是品质都远胜png。 之前自己也总结了一下使用svg的各种方法吧: 1.直接使用 alt=> IE/Edge的...

    Ajian 评论0 收藏0
  • 前端培训-中级阶段5)- jQuery的概念与基本使用(2019-07-11期)

    摘要:前端最基础的就是。前面我们已经基本掌握常规的语法语义,以及基本的使用方法。等价于当载入就绪执行一个函数回调。返回一组匹配的元素。据提供的原始标记字符串,动态创建由对象包装的元素。同时设置一系列的属性事件等。 前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提...

    church 评论0 收藏0
  • 前端培训-中级阶段5)- jQuery的概念与基本使用(2019-07-11期)

    摘要:前端最基础的就是。前面我们已经基本掌握常规的语法语义,以及基本的使用方法。等价于当载入就绪执行一个函数回调。返回一组匹配的元素。据提供的原始标记字符串,动态创建由对象包装的元素。同时设置一系列的属性事件等。 前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提...

    HmyBmny 评论0 收藏0

发表评论

0条评论

2i18ns

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<