V8引擎的垃圾回收策略
V8 的垃圾回收策略主要基于分代式垃圾回收机制。所谓分代式,就是将内存空间分为新生代和老生代两种,然后采用不同的回收算法进行回收。
新生代空间
新生代空间中的对象为存活时间较短的对象,大多数的对象被分配在这里,这个区域很小但是垃圾回特别频繁 。
它将堆内存一分为二,每一部分空间称为 semispace,其中一个处于使用状态(from 空间),另一个处于闲置状态(to 空间)
对于新产生的对象,将从 from 空间中分配内存 。
新生代分配内存非常容易,我们只需要保存一个指向内存区的指针,不断根据新对象的大小进行递增即可。当该指针到达了新生代内存区的末尾,就会触发一次垃圾回收。
新生代的垃圾回收采用 Scavenge 算法 ,其工作原理如下:
首先检查 from 空间,将存活对象复制到 to 空间,非存活对象将会被释放。完成复制后,from 空间和 to 空间角色发生转换。新产生的对象始终从 from 空间中分配内存,to 空间则处于闲置状态。当再次进行垃圾回收时,也会执行和第一次同样的操作,如果存在以下两种情况,存活对象就会被复制到老生代空间中,这个过程称为对象晋升。
- 存活对象已经经历过一次 Scavenge 回收 。
- to 空间内存占用比例超过 25% (保证下次新对象有足够的空间可分配)
老生代空间
老生代空间中的对象为存活时间长或常驻内存对象,大多数从新生代晋升的对象会被移动到这里。
老生代占用内存较多,如果使用 Scavenge算法,不仅会浪费一半空间,复制如此大块的内存消耗时间将会很长,所以 Scavenge 算法显然不适合。
V8 对于老生代中的垃圾回收,采用 Mark-Sweep (标记清除) 和 Mark-Compact(标记整理) 相结合 。
【1】Mark-Sweep
Mark-Sweep 分为 标记 和 清除 两个阶段 。
在标记阶段需要遍历堆中的所有对象,并标记那些活着的对象,然后进入清除阶段。在清除阶段,只清除没有被标记的对象。由于标记清除只清除死亡对象,而死亡对象在老生代中占用的比例很小,所以效率较高。
标记清除存在的问题是,进行一次标记清除后,内存空间往往是不连续的,会出现很多的内存碎片。如果后续需要分配一个需要内存空间较多的对象时,如果所有的内存碎片都不够用,将会使得V8无法完成这次分配,提前触发垃圾回收。
【2】Mark-Compact
标记整理正是为了解决标记清除所带来的内存碎片的问题。标记整理在标记清除的基础进行修改,将其的清除阶段变为紧缩极端。在整理的过程中,将活着的对象向内存区的一段移动,移动完成后直接清理掉边界外的内存。紧缩过程涉及对象的移动,所以效率并不是太好,但是能保证不会生成内存碎片。
三种回收策略比较

从图中可以看出,在 Mark-Sweep 和 Mark-Compact 之间,由于 Mark-Compact 需要移动对象,所以它的执行速度最慢。
所以在取舍上,V8 主要使用 Mark-Sweep,在空间不足以对新生代中晋升过来的对象进行分配时才使用 Mark-Compact 。
垃圾回收引起的性能问题
为了避免出现 JavaScript 应用逻辑 与 垃圾回收操作 产生不一致的冲突,垃圾回收的三种基本算法都需要将应用逻辑暂停下来,待垃圾回收完成后,再恢复执行应用逻辑,这种行为被称为全停顿 。
按官方说法,以 1.5G 的垃圾回收堆内存为例,V8 做一次小的垃圾回收需要 50ms 以上,做一次非增量式垃圾回收甚至需要 1s 以上。这是垃圾回收中引起的 JavaScript 线程暂停执行时间,在这样的时间花销下,应用性能和响应能力都会直线下降。
在 V8 的分代式垃圾回收中,一次小垃圾回收只收集新生代,由于新生代默认配置的较小,且其中活动对象通常较少,所以即便它是全停顿,影响也不大。
但 V8 的老生代通常配置较大,且存活对象较多,全堆垃圾回收的标记、清理、整理等动作造成的停顿就会比较严重。
为降低全堆垃圾回收而导致的停顿时间,V8 做了以下改善措施:
【1】限制堆内存大小
- 新生代:64 位系统 和 32 位系统分别为 32M 和 16 M (from 和 to 空间各占一半)
- 老生代:64 位系统 和 32 位系统分别为 1400M 和 700 M
【2】增量式垃圾回收
V8 先从标记阶段入手,将原来一口气停顿完成的动作改为 增量标记(Incremental Marking),也就是拆分为许多小步进,每做完一步进,就让 JavaScript 应用逻辑执行一小会儿,垃圾回收与应用逻辑交替执行,直到标记阶段完成。V8 后续还引入 Lazy Sweep(延迟清除)、Incremental Compaction (增量式整理),让清理与整理动作也变成增量式的。同时还计划引入并行标记与并行整理,进一步利用多核性能来降低每次停顿的时间。
垃圾回收的触发条件
作用域: 能形成作用域的函数调用、with 语句 以及 全局作用域。
闭包: V8 无法主动回收内存中的闭包引用和全局变量引用。
内存泄漏
通常,造成内存泄漏的原因有如下几个:
- 队列消费不及时
- 作用域未释放
原创发布 @一像素 2017.08
参考文献:
[1] 朴灵,深入浅出Node.js,人民邮电出版社,2013
V8引擎的垃圾回收策略的更多相关文章
- 一文搞懂V8引擎的垃圾回收
引言 作为目前最流行的JavaScript引擎,V8引擎从出现的那一刻起便广泛受到人们的关注,我们知道,JavaScript可以高效地运行在浏览器和Nodejs这两大宿主环境中,也是因为背后有强大的V ...
- V8 引擎的垃圾回收机制
V8 引擎将内存分为新生代和老生代 由于不同对象的生存周期不同,只用一种回收策略来解决问题,这样效率会很低.所以V8采用了一种代回收的策略,将内存分为两个生代:新生代(new generation)和 ...
- JVM的stack和heap,JVM内存模型,垃圾回收策略,分代收集,增量收集
(转自:http://my.oschina.net/u/436879/blog/85478) 在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认 ...
- Java中的分代垃圾回收策略
一.分代GC的理论基础 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大 ...
- JVM分代垃圾回收策略的基础概念
由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略.本文介绍了分代策略的目标,如何分代,以及垃圾回收的触发因素. 文章总结了JVM垃圾回收策略为什么要分代,如何分代,以及垃圾回收 ...
- 浅谈jvm中的垃圾回收策略
下面小编就为大家带来一篇浅谈jvm中的垃圾回收策略.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧 java和C#中的内存的分配和释放都是由虚拟机自动管理的,此前我已 ...
- JVM内存管理中的垃圾回收策略
JVM垃圾回收策略 1.静态内存分配和回收 编译时已经确定了内存空间大小,程序被加载后则一次性分配好内存空间.程序结束后,则对应栈帧撤销,分配的静态内存空间则被回收. 2.动态内存分配和回收 程序运行 ...
- 速记JVM内存模型和垃圾回收策略
一.常用JVM参数 -Xms: 初始堆大小 -Xmx: 最大堆-Xmn: 新生代大小 -Xss: 栈容量 -PermSize: 方法区大小 -MaxPermSize: 最大方法区大小 -MaxDire ...
- 深入理解java虚拟机学习笔记(二)垃圾回收策略
上篇文章介绍了JVM内存模型的相关知识,其实还有些内容可以更深入的介绍下,比如运行时常量池的动态插入,直接内存等,后期抽空再完善下上篇博客,今天来介绍下JVM中的一些垃圾回收策略. 一. ...
随机推荐
- 关于TCP/IP,必知必会的十个经典问题[转]
关于TCP/IP,必知必会的十个问题 原创 2018-01-25 Ruheng 技术特工队 本文整理了一些TCP/IP协议簇中需要必知必会的十大问题,既是面试高频问题,又是程序员必备基础素养. 一 ...
- Spring Security4.1.3实现拦截登录后向登录页面跳转方式(redirect或forward)返回被拦截界面
一.看下内部原理 简化后的认证过程分为7步: 用户访问网站,打开了一个链接(origin url). 请求发送给服务器,服务器判断用户请求了受保护的资源. 由于用户没有登录,服务器重定向到登录页面 填 ...
- redis基础操作~~数据备份与恢复、数据安全、性能测试、客户端连接、分区
数据备份与恢复 数据备份redis save 命令用于创建当前数据库的备份. redis 127.0.0.1:6379> SAVE OK 该命令将在 redis 安装目录中创建dump.rdb文 ...
- switch case 支持的 6 种数据类型!
有粉丝建议可以偶尔推送一些 Java 方面的基础知识,一方面可以帮助一初学者,也可以兼顾中高级的开发者. 那么今天就讲一下 Java 中的 switch case 语句吧,有忘记的同学正好可以温习一下 ...
- 如何通过织云 Lite 愉快地玩转 TSW
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 织云 Lite & TSW 织云 Lite 是一款轻量型服务管理平台,提供标准化的应用打包操作,可连接持续集成系统,完成线上程序分发 ...
- SimpleDateFormat安全的时间格式化
SimpleDateFormat安全的时间格式化 想必大家对SimpleDateFormat并不陌生.SimpleDateFormat 是 Java 中一个非常常用的类,该类用来对日期字符串进行解析和 ...
- 简化异常处理的Throwables类
简化异常处理的Throwables类 有时候, 当我们我们捕获异常, 并且像把这个异常传递到下一个try/catch块中.Guava提供了一个异常处理工具类, 可以简单地捕获和重新抛出多个异常.例如: ...
- BZOJ_2599_[IOI2011]Race_点分治
BZOJ_2599_[IOI2011]Race_点分治 Description 给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 10 ...
- I春秋——Misc(贝斯家族)
不好意思呀!最近一直忙着学习python,竟然忘记更新I春秋里面的题目了(QAQ),我以后会时时刻刻提醒自己要更新 永远爱你们的! ----新宝宝 1:贝斯家族: 解题思路:我相信看见这一题都能够想到 ...
- linux上MongoDB安装部署
①下载安装包 mongodb-linux-x86_64-rhel62-3.6.3.tgz ②解压: tar zxvf mongodb-linux-x86_64-rhel62-3.6.3.tgz ...