Author: Yu Li
date: 2020/04/17
栈是局部的,计算机自动分配,一般2M,较小。堆是全局的,又程序员手动申请和释放,内存动态较大,但是耗时更长。
进程如火车,线程如车厢
一个进程可以由多个线程构成
进程是计算机分配资源的最小单位
线程是进程中用于分配任务的,一个进程中的多个线程共享内存、资源
渲染引擎(GUI)线程:顾名思义,该线程负责页面的渲染
JS引擎线程:负责JS的解析和执行
定时触发器线程:处理定时事件,比如setTimeout, setInterval
事件触发线程:处理DOM事件
异步http请求线程:处理http请求
A-Z: 65-90
a-z: 97-122
对照表:0-63
A-Z a-z 0-9 + /
原理:文本-》ASCII-》八位二进制-》六位二进制-》ASCII-》对照表查询
如果字节不足3个,则填充最前面的六位整数(补0),一位都没有就用=表示
文本
M a n
ASCII码为
77 97 110
转二进制,每个数不满八位的前面补0
01001101 01100001 01101110
转为
010011 010110 000101 101110
再把二进制转为十进制
19 22 5 46
再根据base64编码的对照表,得到对应的字符
T W F u
即 Man 的base64编码为 TWFu
内存触发极限就会垃圾回收。
标记-清除算法
:这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。
这个算法比 计数垃圾收集 要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。
只要闭包的引用赋值为null,闭包内的变量就会被回收,例
function A(){
let a = 0
let func = function(){
return a++
}
return func
}
let b = A()
b()//0
b()//1
b()//2
b = null//回收
let t = A()
t()//0,这里从0开始,说明A()内的a变量已经被回收了,这里重新初始化了
t()//1
...
根对象包括:DOM元素、全局对象(Node中的global和浏览器中的window)、以及全局变量。
垃圾回收是一个全停顿过程
,会阻塞线程
V8的回收策略:分代回收策略
,分为新生代和老生代两块内存,起初都被分到新生代,满足某些条件后移到老生代。
新生代:大多数对象被分配到这里,这个区域很小但是垃圾回特别频繁。根据存入的对象大小移动指针,达到内存区末尾就会清理一次新生代。
新生代实际垃圾回收过程
:有一个From和一个to空间,各占一半空间,回收时会从From空间的根对象广度优先(BFS)遍历(复制完成后会进行紧缩)。To空间会有首尾两个指针,两指针相遇则完成遍历。From区剩余的对象会被回收。完成复制后两个区角色翻转。
对象的晋升
当一个对象经过多次新生代的清理依旧幸存,这说明它的生存周期较长,也就会被移动到老生代,这称为对象的晋升。具体移动的标准有两种:
(1)对象从From空间复制到To空间时,会检查它的内存地址来判断这个对象是否已经经历过一个新生代的清理,如果是,则复制到老生代中,否则复制到To空间中
(2)对象从From空间复制到To空间时,如果To空间已经被使用了超过25%,那么这个对象直接被复制到老生代
老生代
:标记清除分为标记和清除两个阶段。
在标记阶段需要遍历堆中的所有对象,并标记那些活着的对象
,然后进入清除阶段。在清除阶段总,只清除没有被标记的对象
。由于标记清除只清除死亡对象,而死亡对象在老生代中占用的比例很小,所以效率较高。
标记清除有一个问题就是进行一次标记清楚后,内存空间往往是不连续的,会出现很多的内存碎片
。如果后续需要分配一个需要内存空间较多的对象时,如果所有的内存碎片都不够用,将会使得V8无法完成这次分配,提前触发垃圾回收
。
内存泄露原因:
(1)缓存
(2)全局变量(没有声明的变量会转为全局变量,this指向window也会改为全局变量)
(3)计时器引用没有清除(setInterval、DOM元素)
(4)闭包
(5)事件重复监听
避免内存泄漏的方法
(1)少用全局变量,避免意外产生全局变量。
(2)使用闭包要及时注意,有Dom元素的引用要及时清理。
(3)计时器里的回调没用的时候要记得销毁。
(4)为了避免疏忽导致的遗忘,我们可以使用 WeakSet 和 WeakMap结构,它们对于值的引用都是不计入垃圾回收机制的,表示这是弱引用。
Cookie可以携带在同源请求中,而webStorage不会携带
Cookie只有4k,webStorage有5M
sessionStorage仅仅是会话级别窗口,浏览器关闭后就失效;localStorage始终有效,手动删除才会失效;Cookie可以设定有效期限
sessionStorage只能在同一个窗口内共享;localStorage和Cookie在所有同源窗口是共享的
webStorage支持事件通知机制,可以将数据更新的通知发送给监听者
webStorage提供setItem、getItem、removeitem、clear等方法,Cookie需要自己封装setCookie和getCookie
https://mp.weixin.qq.com/s/LawbDakuqmOLKaW0xBbzGg
session缺点
:
(1)服务端需要缓存session,且需要实现持久化存储。
(2)sessionid存在cookie有csrf风险
jwt优势
:
(1)服务端无状态,不存储用户信息
(2)开销小,易于扩展
jwt组成
:
(1)header(base64url)
定义加密算法、类型
(2)payload(base64url)
用户标识信息(如用户名、userid等,但不能放密码在里面)、token过期时间等
(3)signature(base64url)
// 利用hs256加密算法,结合secret密匙,对header.payload进行加密
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
最终返回给前端的jwt如下:
header.payload.signature
此后,前端每个请求要带上Authorization请求头,格式如下:
Authorization: Bearer <token>
后端鉴权原理
:
(1)对接收到的token,进行decode(token),得到header、payload和signature三个字符串
(2)对header和payload,利用secret再做一次加密,并判断加密后的字符串是否和signature一致,如果一致证明通过鉴权,否则未通过
base64url原理
:
对base64中的'+'=>'-','/'=>'_','='=>'',理由:如果base64url放在url中传递,'+'和'/'会被转义
jwt存储
:
如果存在内存中,网页关闭就丢失了token;要实现登录态持久缓存,只能存在cookie或者webStorage中
(1)存在cookie中,因为网页本身要读取cookie,所以不能设置http-only,所以虽然可以抵御csrf(默认只是通过请求带上cookie响应头来伪造请求),但是无法抵御xss
(2)存在webstorage,同样可以抵御csrf,但是无法抵御xss
当然,抵御xss可以从很多方面来解决,只要确保网页不执行未知的js脚本即可,比如不使用document.write,或者对dom操作的文本都做转义处理。