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中的一些垃圾回收策略. 一. ...
随机推荐
- SSM-Spring-17:Spring中aspectJ注解版
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- AspectJ AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP 语法,能够在编译期提供 ...
- PiggyMetrics windows 部署
PiggyMetrics 是springcloud的demo,其特性就不细说了,主要描述在win10下部署的坑. 官网是:https://github.com/sqshq/PiggyMetrics 官 ...
- Supervisor管理进程
Supervisor管理进程 转载 2016年04月14日 18:26:45 标签: supervisord 28344 Supervisor重新加载配置启动新的进程 liaojie 发布于 1年前, ...
- Spring Boot 使用 Log4j2
Java 中比较常用的日志工具类,有 Log4j.SLF4j.Commons-logging(简称jcl).Logback.Log4j2(Log4j 升级版).Jdk Logging. Spring ...
- linux.go
func LockFile(file *os.File) error { return syscall.Flock(int(file.Fd()), syscall.LOCK_EX) }
- xshell 快速复制打开之前用过的ssh
有时候需要对一个ip地址的服务器进行多个操作,不在一个会话中,需要新建多个同样的会话 然后左键单击+号 出现一个新的会话 然后再右键单击+号 选择之前保存过的ip地址即可快速创建!
- 神奇的Scala Macro之旅(三)- 实际应用
在上一篇中,我们示范了使用macro来重写 Log 的 debug/info 方法,并大致的介绍了 macro 的基本语法.基本使用方法.以及macro背后的一些概念, 如AST等.那么,本篇中,我们 ...
- 【JVM虚拟机】(9)-- JVM是如何处理异常的
[JVM虚拟机](9)-- JVM是如何处理异常的 上篇博客我们简单说过异常信息是存放在属性表集合中的Code属性表里,那么这篇博客就单独讲Code属性表中的exception_table. 在讲之前 ...
- python导出zabbix数据并发邮件脚本
Zabbix没有报表导出的功能,于是通过编写脚本导出zabbix数据并发邮件.效果如下: 下面是脚本,可根据自己的具体情况修改: #!/usr/bin/python #coding:utf-8 imp ...
- redis基本类型以及优点特性
1.什么是redis? redis是一个基于内存的高性能key-value数据库 2.redis基本数据类型及应用场景 支持多种数据类型: string(字符串) String数据结构是简单的k ...