内存清理对于任何应用程序来说都是强制性的,包括那些用JS编写的应用程序。如果没有内存清理,则内存中将充满对象(大多数是“死”对象),在某些时候,甚至于会导致根本没有空间存放新数据。

在本文中,我们将介绍清理JS内存的基本原理,并通过分析一些垃圾回收算法来确定哪种效率更高。

什么是垃圾

垃圾指的是无法通过“root”引用而获取的任意对象。垃圾包括与根对象失去联系的所有“死对象”。

此外,“死”对象通常彼此链接,并形成相当大尺寸的链。不过,这并不能使它们“死而复生”,这也是为什么引用计数方法无法进行垃圾收集的原因。那么,哪些方法奏效呢?且听我一一分析。

垃圾收集方法

垃圾收集的方法有不少,首先介绍的是最简单、最快的方法,最后再讲一讲最有效但又最慢的方法。

1 Mark and Sweep(标记-清除)

这是最简单的垃圾搜索和删除算法。它仅在内存包含垃圾的情况下起作用;如果只有活动对象,那么Mark and Sweep算法不会执行任何操作。

Mark and Sweep的主要缺点是会分散内存。因此,尽管它可以清理出大量空间,但是仍然无法将新的大型对象存储在RAM中。

2 Mark-Sweep-Compact(标记-清除-整理)

Mark-Sweep-Compact算法具备非常必要的碎片整理功能。在清除垃圾后,它会移动所有剩余的对象,并创建一大块可用的内存空间。

就结果而言,Mark-Sweep-Compact是最佳的。但是,转移对象的过程相当耗资源。此外,Mark-Sweep-Compact需要遍历内存两到三遍,这需要额外耗费大量的时间。

3 SemiSpace

SemiSpace(复制收集器)在某种程度上加快了清理和碎片整理的过程。SemiSpace会查找活动对象,将它们复制到预先分配的空间,然后删除剩余的对象。

该算法比Mark-Sweep-Compact更简单,更快,但是需要额外的空间来复制活动对象。此外,复制过程也很耗资源。

4 Weak Generational Hypothesis(弱代假设)

根据代际假说(Generational Hypothesis),新对象比老对象更容易死亡。新创建的对象更有可能成为垃圾。因此,垃圾收集器更频繁地检查此类对象是合理的。

先看一下运用Java垃圾回收算法的分代方法。其工作方式如下:

  1. 所有对象分为新生代和老生代。

  2. 新生代分为以下几个区:Eden(伊甸区),Survivor Space(幸存者区)1和Survivor Space 2,即S0和S1。

  3. 所有新对象都进入Eden。

  4. 每次检查时,算法会将活动对象从Eden和其中一个幸存者区传输到另一个幸存者区,然后彻底清理已检查过的区。

  5. 此算法依次检查幸存者区:如果在上一次检查期间检查了Eden和S0,并将活动对象复制到S1,则在下一次检查期时,将检查Eden和S1,并将活动对象复制到S0。

  6. 可以生存很长时间的对象会成长为老生代。在这段时间里,它们会经过多次检查,并且被一直保留下来。

在V8 JS引擎中运行的Orinoco GC垃圾收集器,应用的原理类似。

5 Orinoco GC

Orinoco GC会分割内存并根据对象的年龄移动对象,但是此算法有其细微差别之处:

  1. 内存分为新内存和老内存,但新内存仅包含两个区:Nursery和Intermediate。

  2. 一次在多个线程中执行内存检查。当将活动对象从Nursery移动到Intermediate时,流将留下一个前向指针。这样做是为了使到达同一个对象但来自不同引用链的另一个线程不会尝试再次移动它。

Orinoco GC算法的工作原理如下:

  1. 所有的新对象都去到Nursery,然后在检查了Scavenger之后,尚存的对象移到了Intermediate。其余的将被删除。

  2. 压缩内存并通过下一个Scavenger检查后,活动对象将转到旧内存。

  3. 减少对旧内存的检查频率;且检查由主GC进行。

Orinoco中有两个垃圾收集器:一个是辅助的(Scavenger),用于检查新内存;另一个是主要的,用于检查整个数组。Compact阶段是可选的,是主收集器的特权。由于移动对象仍然是一个消耗资源的过程,因此为了最小化此类动作,Orinoco使用了空闲列表。这些是内存中可用空间的列表,这些列表允许为新对象定义合适的可用空间。如果内存过于分散并且对象没有合适的位置,则算法会进入Compact阶段。

JS开发人员无权访问GC; 这是一个实现细节。尽管JS无法直接调用收集器,但是V8提供了访问引擎所嵌入的环境的途径。GC可以设置环境在空闲时间执行的任务。

©著作权归作者所有:来自51CTO博客作者mb5ff5930cde1cd的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 芋道 Spring Boot 对象转换 MapStruct 入门
  2. 【第767期】你不懂JS:混合(淆)“类”的对象
  3. 【第766期】你不懂JS:对象
  4. 在 JavaScript 中对象的深拷贝(及其工作原理)[每日前端夜话0x8F]
  5. yarn-site.xml的部分资源配置参数,主要是与内存相关
  6. Redis 哈希结构内存模型剖析
  7. C#基础入门第十二天(面向对象多态,File操作流,泛型集合)
  8. 5个小技巧彻底搞懂JVM内存模型【针对3年以上Java开发】
  9. 小心递归中内存泄漏

随机推荐

  1. Android下SQLite数据库编程学习系列之二-
  2. Android中的MVC、MVP、MVVM
  3. 用PHP编写Android应用程序
  4. Android—最新版Android studioSDK下载与
  5. 《Android移动应用基础教程》(Android Stu
  6. (三)Android事件分发机制 - Activity篇
  7. [置顶] 总结的一些android公共库
  8. Android ADT插件配置
  9. 将要更新到android 4.0的手机列表
  10. 在 Android 4.1上,分析 input -- android