大家好,今天分享的主题为 JavaScript 内存管理机制,本次分享将从以下三部分进行讲述:

  • js 内存管理与 js 垃圾

  • 常见的 GC 算法

  • V8 引擎的垃圾回收

js 内存管理与 js 垃圾

关于 JavaScript 内存管理机制,相信大家都有所了解。我们就简单看一下 js 内存管理与 js 垃圾。JavaScript 内存管理是由 JS 自动操作的,不需要人为进行参与,这些内存管理包含以下三项:

  • 申请内存空间

  • 使用内容空间

  • 释放内容空间

而 js 垃圾是指对象不在引用时、对象不能从根上访问到时,都可以被称为 js 垃圾。其他部分包括引用和可达对象这些大家肯定很熟悉了,我们就不再多说。下面我们谈一谈 GC 算法。

GC 算法

GC 算法其实是为了找到内存中的垃圾,并释放和回收空间。这里所说的的垃圾,是指算法中认为程序中不再需要使用的对象,与程序中不能访问到的对象。

说回 GC 算法,这个是比较概念性的内容,我们简单归纳一下。GC 是一种自内存中查找垃圾释放空间、回收空间的一个垃圾回收器机制。算法则是工作时查找和回收所遵循的规则。常见 GC 算法有引用计数、标记清除、标记整理、分代回收。

引用计数

引用计数曾经主要用于 IE8 以下的浏览器,现在的浏览器已不再使用,因此只做简单介绍。引用计数的基本原理是记录跟踪每一个值被引用的次数,被引用则计数加一,被释放则减一,当数值为零时则代表该值所在内存已经不再使用,因此释放所占空间。引用计数的优点是引用次数实时监控,所以回收垃圾能够及时回收,从而最大限度减少程序暂停卡顿时间。但也是因为一直在运作,所以资源消耗和时间开销大,无法回收循环引用的对象。

标记清除

标记清除分为分为标记和清除两个阶段,其核心思想是遍历所有对象,找标记活动对象,即前面提到的可达对象,清除没有标记的对象,以及回收没有标记对象的空间。

上图是 global 的查找简易流程图。其中左侧 A、B、C、D、E 表示可查到的对象,右侧 a1、b1 表示循环引用对象。其中 a1 为引用计数,而因为引用计数一直在运作,无法回收循环引用的对象的缺点,可以反向找到正在循环引用的对象。

这也是标记清除的优点,可以解决对象循环的引用回收问题。但是标记清除的缺点是空间碎片化,无法及时回收垃圾对象。因为它需要先标记再清除,不能像引用计数一样对值进行实时监控,因此无法让空间最大化使用。通过下图可以简单看一下标记清除的空间碎片化特点。

标记整理

上面提到标记清除有空间碎片化的缺点,而标记整理优化了这个缺点。从名字也可以联想到,标记整理是标记清除的增强。标记整理在标记阶段的操作和标记清除一致,但是在清除阶段会先执行整理,再进行清除。这种方式能够有效减少碎片化空间。和标记清除一样,标记整理也不能实时回收垃圾对象。

我们通过下面三张图对标记整理进行一个简单直观的了解。

可以看到在进行垃圾回收前,活动空间和非活动空间是混杂的。而在确定进行回收后,标记整理会对空间进行归类整理,将活动空间和非活动空间统一整理到一起,形成下图的结果:

之后再进行标记清除就能够避免回收操作避免出现大量碎片化空间,让空间最大化应用。

看完了 GC 算法,以 V8 引擎为例我们具体来看一下 GC 算法在 JS 垃圾回收里的使用。

V8 引擎的垃圾回收

V8 是一款当下较为主流 JavaScript 执行引擎,采用即时编译,处理速度很快。V8 的内存是设限的,比如 64 位操作系统的上限是 1.4T,下限是 700M,32 位操作系统的上下限分别为 64M 和 32M。

V8 采用分代回收的垃圾回收策略,将内存分为新生代和旧生代两种,并对不同的对象采用不同的对应算法。

上图是 V8 的内存分配示意图,可以清除看到 V8 内存空间分为两部分。左边的 from 和 to 是新生代,占用的空间比较小(32M|16M),这里的新生代指的是存活时间短的存储区。右边红色的部分则是存活时间较长的老生代存储区。

V8 常用的 GC 算法有以下 5 种:

  • 分代回收

  • 空间复制

  • 标记清除

  • 标记整理

  • 标记增量

这其中新生代采用复制算法和标记整理进行垃圾回收,老生代使用标记清除、标记整理和增量标记进行垃圾回收。

V8 新生代对象回收实现

上图为 V8 新生代对象回收实现图,采用复制算法和标记整理结合的方式进行垃圾回收。新生代内存区的两个等大空间,From 代表使用空间用于存储活动对象,To 代表空闲空间。V8 的新生代对象回收是通过标记整理将对象完成整理后拷贝到 To,然后将 To 和 From 进行空间交换,并释放整理后的无用对象所占空间。需要注意的是,在将整理对象拷贝到 To 时可能会出现晋升。晋升指的是将新生代对象移动至老生代存储区。晋升通常有两个条件,其一是在进行一轮 GC 后还活着的新生代对象可以晋升,其二是 To 空间的使用率超过 25%。

V8 老生代对象回收实现

V8 老生代的回收过程采用标记清除、标记整理和标记增量结合的方式。一般在进行垃圾回收时会通过标记清除完成垃圾空间的回收,但是当新生代移动到老生代,而老生代内存不够时,则会通过标记整理进行空间优化,并使用增量标记进行效率优化。

标记增量其实是通过对标记操作进行标记的方法,让时间安排变得合理。这句话可能有些绕,简单说就是在垃圾回收时,让标记系统在标记时分出不同的时间段,分别进行标记和执行,让二者的操作间隔开,从而优化时间安排,这会让页面在体感上更为顺畅。

推荐阅读

OpenShift 与 OpenStack:让云变得更简单

如何处理大体积 XLSX/CSV/TXT 文件?

javaScript 内存管理机制的更多相关文章

  1. [原创作品]Javascript内存管理机制

    如果你也喜欢分享,欢迎加入我们:QQ group:164858883 内存策略:堆内存和栈内存栈内存:在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个 ...

  2. python的内存管理机制

    先从较浅的层面来说,Python的内存管理机制可以从三个方面来讲 (1)垃圾回收 (2)引用计数 (3)内存池机制 一.垃圾回收: python不像C++,Java等语言一样,他们可以不用事先声明变量 ...

  3. python的内存管理机制(zz)

    本文转载自:http://www.cnblogs.com/CBDoctor/p/3781078.html 先从较浅的层面来说,Python的内存管理机制可以从三个方面来讲 (1)垃圾回收 (2)引用计 ...

  4. 浅谈Linux内存管理机制

    经常遇到一些刚接触Linux的新手会问内存占用怎么那么多?在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这 ...

  5. ARC内存管理机制详解

    ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数. ...

  6. 深入了解C#系列:谈谈C#中垃圾回收与内存管理机制

    今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个<WCF分布式开发必备知识>系列后的一次休息吧.以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会 ...

  7. 【Cocos2d-x 3.x】内存管理机制与源码分析

    侯捷先生说过这么一句话 :  源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...

  8. Spark 1.6以后的内存管理机制

     Spark 内部管理机制 Spark的内存管理自从1.6开始改变.老的内存管理实现自自staticMemoryManager类,然而现在它被称之为"legacy". " ...

  9. Java虚拟机内存管理机制

    自动内存管理机制 Java虚拟机(JVM)在执行Java程序过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区 ...

随机推荐

  1. Redhat7 安装 yum源(亲测有效)

    由于之前安装Redhat7 想安装vsftpd  使用yum   install vsftpd 报错: This system is not registered to Red Hat Subscri ...

  2. markdown类型文件编辑大全

    正文: 1.标题的几种写法: 第一种:     前面带#号,后面带文字,分别表示h1-h6,上图可以看出,只到h6,而且h1下面会有一条横线,注意,#号后面有空格 第二种:      这种方式好像只能 ...

  3. 航模电池平衡头接线,1S-6S原理图

    1-4S平衡头接线 S数代表几级锂电池串联,比如3S代表串联了3级,所以总电压为3*3.7V=11.4V.(每一级可能是多个电芯并联) 图源:百度贴吧 图源:5imx论坛 3S电池示例 B6充电器

  4. Java9的模块化是什么

    Java9新特性中的模块化到底是什么 Java9中的一个重大特性是增加了一种新型的程序设计组件 - 模块. 官方对模块的定义为:一个被命名的,代码和数据的自描述集合.( the module, whi ...

  5. IDEA端口占用的解决方案

    使用端口查找到对应的进程PID: netstat -ano | findstr "端口" 查找到对应PID的进程信息 tasklist /v /fi "PID eq &l ...

  6. 阐述 ArrayList、Vector、LinkedList 的存储性能和特性?

    ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的 数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉 及数组元素移动等内存操作,所以索引数 ...

  7. 你如何确保 main()方法所在的线程是 Java 程序最后结束 的线程?

    我们可以使用 Thread 类的 join()方法来确保所有程序创建的线程在 main()方法退出前结束.

  8. java的arrays

    java.util.Arrays 是一个于数组相关的工具类,里面提供大佬的静态方法,用来实现数组常见的操作 public staic String toString(数组)  将参数数组编程字符串,默 ...

  9. Java中如何强制类型转换

    例如,当程序中需要将 double 型变量的值赋给一个 int 型变量,该如何实现呢? 显然,这种转换是不会自动进行的!因为 int 型的存储范围比 double 型的小.此时就需要通过强制类型转换来 ...

  10. Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法 ?

    1.关闭缓存 swap; 2.堆内存设置为:Min(节点内存/2, 32GB); 3.设置最大文件句柄数: 4.线程池+队列大小根据业务需要做调整: 5.磁盘存储 raid 方式--存储有条件使用 R ...