手机网站建设在哪儿,thinkphp购物网站开发视频,制作网页时文字可以做超链接吗,台州网站建站公司V8引擎垃圾回收机制
v8引擎负责JavaScript的执行。V8引擎具有内置的垃圾回收机制#xff0c;用于自动管理内存分配和释放
堆与栈
栈空间
栈空间是小而连续的内存空间#xff0c;主要用于存储局部变量和函数调用的相关信息#xff0c;同时栈结构是“先进后出”的策略
栈…V8引擎垃圾回收机制
v8引擎负责JavaScript的执行。V8引擎具有内置的垃圾回收机制用于自动管理内存分配和释放
堆与栈
栈空间
栈空间是小而连续的内存空间主要用于存储局部变量和函数调用的相关信息同时栈结构是“先进后出”的策略
栈空间的最大的特点是空间连续所以在栈中每个元素的地址都是固定的因此栈空间的查找效率非常高但是通常在内存中很难分配到一块很大的连续空间因此V8 对栈空间的大小做了限制如果函数调用层过深那么 V8 就有可能抛出栈溢出的错误
分配和释放速度快栈上的资源分配和销毁只需要移动指针因此速度非常快。固定大小栈的空间是有限的一旦函数调用层次过多或数据过大就会导致栈溢出。遵循LIFO原则后进先出即最后进入栈的元素会被最先弹出。
堆空间
堆空间是一种树形的存储结构用来存储对象类型的离散的数据
动态分配堆上的内存空间是动态分配的可以根据需要分配不同大小的内存块。灵活性高堆可以存储各种类型的数据包括对象、数组等复杂数据结构。管理复杂由于堆的空间是动态分配的管理起来相对复杂容易出现内存泄漏等问题
回收策略
1》标记清除
这种算法在 JS 引擎中是最常用的大部分浏览器都是V8引擎使用的是标记清除
大致过程
首先标记内存中所有的变量假设所有的都是垃圾都标记为0
然后将在上下文中的变量以及被上下文引用的变量的标记清除
在此之后离开环境时不再当前环境定义的变量被标记清除的再被加上标记的变量就是待删除了
优点
实现比较简单打标记也无非打与不打两种情况这使得一位二进制位0和1就可以为其标记非常简单
缺点
标记清除算法有一个很大的缺点就是在清除之后剩余的对象内存位置是不变的也会导致空闲内存空间是不连续的出现了 内存碎并且由于剩余空闲内存不是一整块它是由不同大小内存组成的内存列表这就牵扯出了内存分配的问题
标记整理算法在标记结束后将活着的对象向内存的一端移动将不需要清理的对象往内存一端移动最后清理掉边界的内存
2》引用计数
大致过程
声明变量并赋值的时候引用次数为1
当同一个值被赋值给另一个比那辆引用书加1
如果对该值引用的变量被其他值覆盖了引用书减1
如果引用次数为0时就可以被回收了
优点
引用计数算法的优点我们对比标记清除来看就会清晰很多首先引用计数在引用值为 0 时也就是在变成垃圾的那一刻就会被回收所以它可以立即回收垃圾
缺点
首先它需要一个计数器而此计数器需要占很大的位置因为我们也不知道被引用数量的上限
其次循环引用问题对象A有一个指针指向对象B而对象B也引用了对象A
对象A、B的引用次数都是2在函数执行后其实引用次数仍然时2但是它们其实都不在作用域中了属于可以回收的垃圾但是内存并没有被释放 function circleRef() {let A new Object()let B new Object()A.a BB.b A}circleRef()想要释放必须在最后去掉引用关系
Anull
BnullIE8及以前是使用的引用清除
V8引擎内存空间划分
**1》新生代new space**用于存储新创建的对象。分为两部分分别是使用去和空闲区
**2》老生代old space**存储生存时间较长的对象。这些对象在新生代中存活一段时间后会被移动到老生代。老生代内存区域相对较大因为存储的是长期存活的对象所以垃圾回收的频率相对较低。老生代内存区又分为老生代指针区和老生代数据区前者包含大多数可能存在指向其他对象的指针的对象后者只保存原始数据对象这些对象没有指向其他对象的指针
**3》大对象区Large Object Space**存放体积超过其他区域大小限制的大对象这些对象由于体积较大不会被频繁移动因此放在单独的区域以避免影响其他对象的垃圾回收
**4》代码区Code Space**存放JavaScript代码这是唯一拥有执行权限的内存区域
**5》Map区Map Space**用于存放Cell和Map每个区域都是存放相同大小的元素结构简单
6》Cell Space和Property Cell Space用于存储固定大小的Cell对象和与JavaScript对象属性相关的PropertyCell对象优化属性访问性能
V8引擎的垃圾回收策略
V8 设置了两个垃圾回收器主垃圾回收器和副垃圾回收器主垃圾回收器负责收集老生代中的垃圾数据回收频率较低副垃圾回收器负责收集新生代中的垃圾数据回收相比更加频繁
可访问性分析法
V8引擎中采用了这种方法来判断是一个对象是否活跃具体过程为将一个称为GC Roots的对象在浏览器环境中GC Roots 可以包括全局的 window 对象、所有原生DOM节点集合等等作为所有初始存活的对象集合从这个对象出发进行遍历遍历到的就认为是可访问的为活动对象需要保留如果没有遍历到的对象就是不可访问的这些就是非活动对象可能就会被垃圾回收。
新生代
新生代主要用于存放存活时间较短的对象。使用scavenge算法进行回收
新生代回收过程
scavenge算法主要使用了一种复制式的方法cheney算法复制式的方法来实现
新生代空间被平等划分为两部分from space 和to space; 当from space填满之后会进行一次垃圾回收非存货对象被回收存活的对象被复制到 to space,from space被清空from space和to space进行了一次交换
scavenge算法是一种典型的牺牲空间换取时间的算法
新生代垃圾回收采用了并行机制。在新生代垃圾回收的过程中副垃圾回收器使用并行机制在整理排序阶段即活动对象从from-space复制到to-space的时候启用多个辅助线程并行进行整理。这种并行处理的方式意味着多个线程同时参与垃圾回收的过程以提高效率。由于多个线程可能竞争同一个新生代的堆内存资源可能会出现某个活动对象被多个线程进行复制操作的情况。为了解决这个问题V8在第一个线程对活动对象进行复制完成后必须去维护复制这个活动对象后的指针转发地址以便于其他协助线程可以找到该活动对象后可以判断该活动对象是否已被复制。这种机制确保了新生代垃圾回收的正确性和效率
对象晋升新生代变成老生代
当一个对象在经过多次复制之后依旧存活那么它会被认为是一个生命周期较长的对象在下一次进行垃圾回收时该对象会被直接转移到老生代中
晋升的条件
对象是否经历过一次Scavenge算法内存占比是否超过To空间的25%避免内存使用过高影响后续的对象分配
满足其中任意一个就会晋升
老生代
老生代采用Mark-Sweep(标记清除)和Mark-Compact(标记整理)来进行管理
老生代回收过程
标记递归遍历一组根元素遍历能达到的元素是活动对象达不到的是非活动对象 (Mark-Sweep)清除老生代垃圾回收器直接将非活动对象数据、垃圾清理掉 (Mark-Sweep)整理(Mark-Compact)
以下几种情况都可以作为根节点
全局对象本地函数的局部变量和参数当前嵌套调用链上的其他函数的变量和参数
Mark-Sweep算法存在一个问题就是在经历过一次标记清除后内存空间可能会出现不连续的状态因为我们所清理的对象的内存地址可能不是连续的所以就会出现内存碎片的问题导致后面如果需要分配一个大对象而空闲内存不足以分配就会提前触发垃圾回收所以通过Mark-Compact(标记整理)解决内存碎片的问题
一般 10 次Mark-Sweep会伴随一次Mark-Compact
策略优化
最开始的垃圾回收机制有两个大的缺点
垃圾回收器在主线程上执行执行垃圾回收期间会阻塞js执行一次执行一个完整的垃圾回收流程阻塞时间还是连续的
对此V8做出了许多优化
一般来说老生代会保存大量存活的对象如果在标记阶段将整个堆内存遍历一遍那么势必会造成严重的卡顿。
因此为了减少垃圾回收带来的停顿时间V8引擎又引入了增量标记 的概念即将原本需要一次性遍历堆内存的操作改为增量标记的方式先标记堆内存中的一部分对象然后暂停将执行权重新交给JS主线程待主线程任务执行完毕后再从原来暂停标记的地方继续标记直到标记完整个堆内存。只有在浏览器的空闲时间才会执行对应的任务否则延迟执行尽可能少地影响主线程的任务避免应用卡顿提升应用性能。
得益于增量标记的好处V8引擎后续继续引入了延迟清理 和 增量式整理让清理和整理的过程也变成增量式的。同时为了充分利用多核CPU的性能也将引入并行标记和并行清理进一步地减少垃圾回收对主线程的影响为应用提升更多的性能
增量标记V8使用增量标记即不是一次性遍历整个堆而是分批次进行使得每次正常执行之间的停顿非常短暂即将原本需要一次性遍历堆内存的操作改为增量标记的方式先标记堆内存中的一部分对象然后暂停将执行权重新交给JS主线程待主线程任务执行完毕后再从原来暂停标记的地方继续标记直到标记完整个堆内存延迟清理在浏览器的空闲时间执行垃圾回收任务增量整理一次整理一部分并发标记允许垃圾回收的标记阶段与JavaScript代码执行并行进行减少了主线程的暂停时间并行处理多个辅助线程并行进行整理提高了垃圾回收的效率