当前位置: 首页 > Web前端 > JavaScript

深入理解JavaScript——垃圾回收机制

时间:2023-03-26 21:47:25 JavaScript

灵魂三问:什么是垃圾回收,什么是回收?你为什么会有这个东西?本文将介绍并尝试回答这三个问题什么是垃圾回收?在讲这个之前,我们需要先解释一下什么是内存泄漏。因为内存泄漏,引擎会回收这些无用的变量。这个过程称为垃圾收集。什么是内存泄漏?程序的运行需要占用内存。这些程序在不用的时候,如果内存没有释放,就会造成内存泄漏。举个通俗的例子,就像占了厕所不拉屎。坑就那么多(内存容量),不出去(释放内存)会导致想拉的人拉不动(系统卡死,严重的会导致进程tocrash)也就是说不再使用的内存没有及时释放,称为内存泄漏。而内存泄漏会导致系统占用极高的内存,导致系统卡死甚至死机。所以就会有垃圾回收机制来帮助我们回收未使用的内存。当我们遇到内存泄漏时,我们需要做什么呢?什么都不用做,因为JavaScript中的垃圾回收是自动的如果你看过《JoJo的奇妙冒险:不灭钻石》,就知道双打有自动双打,比如吉良吉影的双打“杀手皇后”第二形态:凋零透心攻在JavaScript的世界里,JavaScript引擎会自动执行命令来帮助我们清理无用的变量(也就是减少内存开销)。当然,不同的语言使用不同的内存管理方式,大多数语言使用自动内存管理。内存管理(垃圾回收)阵营:JavaScript、Java、Go、Python、PHP、Ruby、C#手动内存管理阵营:C、C++、Rust什么是回收现在我们可以回答第二个问题:什么是回收?回收内存。清理变量并释放内存空间。为什么会有这样的事情?为什么会有垃圾收集?在前面的描述中,我们提到如果允许内存泄漏,系统会卡住甚至崩溃。出现这个问题的原因是JavaScript引擎V8只能使用部分内存。具体来说,在64位系统中,V8最多只能分配1.4G;在32位系统中,最多只能分配0.7G,因为使用的内存大小是上限,所以当有变量没有使用到的时候,引擎会帮我们在这里清理。我们不禁会想,这个东西是怎么工作的呢?我怎么知道我的哪些变量没有被使用?如果清除正在使用的变量会发生什么?带着这个问题,我们来了解一下垃圾回收的运行机制。在讲这个话题之前,我们先回顾一下,JavaScript数据类型可以分为基本类型和引用类型。基本类型存在于栈内存中,引用类型存在于堆内存中,但是我们当时并没有解释为什么基本类型要存放在栈中,而引用类型要存放在堆中。只是介绍一下,因为基本类型消耗内存少,而引用类型消耗内存多,而这也正是在两个空间存储不同数据的原因。在JavaScript中,引擎需要使用栈来维护程序执行过程中的内存。上下文状态(也就是执行上下文),如果栈空间很大,所有存放在栈空间的数据都会影响上下文切换的效率,从而影响整个程序的执行效率,所以占用内存大的数据会放在堆空间中,引用它的地址来表示这个变量堆内存的分类。一个V8进程的内存通常由以下几部分组成:新生代内存区(newspace)、老年代内存区(oldspace)、大对象空间(largeobjectspace)、代码区(codespace)、映射区(mapspace)等几个都不重要,关键是新生代(memory)和老年代(memory)。对于新生代和老年代,引擎采用了两种不同的垃圾回收机制。新生代和老年代的垃圾回收。临时假设具有以下两个特征:大多数对象在内存中的存在时间很短。简单地说,一旦分配了内存,许多对象将很快变得不可访问。不朽的物体会因为代际假设认知而活得更久,所以我们在收集垃圾的时候,会根据物体不同的生命周期使用不同的算法。其中,V8将堆内存分为两个区域:新生代和老年代(其他区域用处不大)新生代中存放生命周期短的对象,中存放生命周期长的对象在老一代。为此,新生代区域通常只支持1~8M的容量,而老年代区域支持更大的容量。针对这两个方面,V8分别使用了两个不同的垃圾收集器。主垃圾收集器负责老年代的垃圾收集。二级垃圾收集器负责新生代的垃圾收集。我们先来说说二级垃圾回收器是如何处理垃圾回收的。新生代的内存被回收。新一代使用Scavenge算法。所谓Scavenge算法将新生代空间分为两块区域,一半是对象区(from),另一半是空闲区(to)。如下图所示:新的对象会先被分配到对象(from)空间,当对象区域快满时,需要进行一次垃圾清理操作。在进行垃圾回收时,首先将from空间中存活的对象复制到free(to)空间中存放,回收非存活空间。复制完成后,对象空间和空闲空间的角色互换,空闲空间变成新的对象空间,原来的对象空间变成空闲空间。这样就完成了垃圾对象的回收操作。同时,这种角色互换操作可以让新生代中的这两个区域可以无限期地重复使用。“晋升”到“老年代区”。这个过程称为对象提升策略老年代内存收集主要的垃圾收集器负责老年代区域的垃圾收集。对象包括新生代区域中“提升”的对象和一些大对象。因此,老年代区的对象有两个特点。物体占据的空间大,寿命长。它没有像新生代区那样使用Scavenge算法,因为复制大对象耗时较长,执行效率不高。所以它使用标记-清除(Mark-Sweep)进行垃圾回收。简单的说就是先标记再清除,但是内存空间中的对象还是不连续的,所以引入排序。这就是老年代区mark-sweep-organize的垃圾回收流程。先标记哪些是要回收的变量,然后回收(clear),再把内存空间整理一下(往一边),这样空间会大一些,因为老年代区的对象比较大,虽然“mark-clear”算法会比Scavenge快,但是经不起滞后问题。为什么卡住了?因为JavaScript是单线程的。为此,V8将标记过程划分为子标记过程,同时允许垃圾回收标记和JavaScript应用逻辑交替进行,直到标记阶段完成。这种算法称为增量标记算法,这种行为类似于ReactFiber的设计思想。大任务分解成小任务。因为它们很小,执行速度很快,人们不会注意到延迟。新一代与老一代。内存中,存活时间长Younggenerationgarbagecollection由secondarygarbagecollector负责;oldgenerationgarbagecollection负责主垃圾收集器Scavenge算法用于newgeneration;“mark-clear”算法用于oldgenerationScavenge算法:将空间分成两半,一半来自空间,一半来自空间。新添加的对象会被放入from空间,当空间快满时,进行垃圾清理;然后交换角色,等交换后的from空间快满的时候再进行垃圾清理,如此反复mark-clean-organize:这个有两种算法,“mark-clean”算法和“mark-organize”算法。空间,排序算法会把内存排序到一个空间里,空间会变大。引用计数(referencecounting)在《JavaScript 高级程序设计》中引入了另一种垃圾回收机制——引用计数简单来说:引擎会有张“引用表”,它保存了资源在内存中的引用次数。如果一个值的引用次数为0,则表示该值不再被使用,那么可以释放内存,但后来这种机制被废弃了,因为会遇到一个严重的问题:循环引用,导致内存泄漏,所以它被放弃来记录垃圾收集的历史。1960年,JohnMcCarthy发表了一篇论文,提出了mark-sweep算法。但是,mark-clear算法有两个致命的缺点:分配速度慢,容易产生碎片。为了解决这个问题,1963年,MarvinL.Minsky提出了复制算法。JavaScript中的Scavenge算法就是在它的基础上改进的版本。它的缺点是空间利用率不高,一次只能使用一次。1960年,GeorgeE.Collins提出了一种新的GC算法:引用计数。缺点是“循环引用”无法恢复。当前的JavaScript引擎不使用它。这种回收机制是这样的。垃圾回收大楼地基已经建成。后人只是在此基础上鼓捣而已。总结什么是垃圾回收机制,为什么要有垃圾回收机制,介绍垃圾回收的运行机制。它的两种内存采用不同的垃圾回收算法等等。了解垃圾回收机制是为了让我们更清楚地了解它的运行原理。虽然我们不需要理解“Mark-Clean”、“Mark-Clean”、“Scavenge”等算法,但是如果我们理解他们为什么使用这个,否则当小白问为什么网站卡时,你能不能“无意”泄露是否是内存泄漏,进而导致JavaScript垃圾回收机制等,伪装成老手的经验参考资料V8内存管理与垃圾回收机制V8引擎垃圾内存回收原理解析高-性能JavaScript引擎V8-垃圾收集漫画详解垃圾收集JavaScript内存泄漏教程垃圾收集:垃圾数据是如何自动收集的?系列文章深入理解JavaScript-开篇深入理解JavaScript-什么是JavaScript深入理解JavaScript-JavaScript由什么组成深入理解JavaScript-一切皆对象深入理解JavaScriptJavaScript之——对象(object)深入理解JavaScript——newdone什么是深入理解JavaScript——Object.create深入理解JavaScript——复制的秘密深入理解JavaScript——PrototypeIn-深入理解JavaScript——继承深入理解JavaScript——JavaScript第一帝深入理解JavaScript——instanceof——寻祖深入理解JavaScript——Function深入理解JavaScript——Scope深入深入理解JavaScript——this关键字深入理解JavaScript——call、apply、bind深入理解JavaScript——执行上下文和callstack深入理解JavaScript——scopeVSexecutioncontext深入理解JavaScript——闭包深入理解JavaScript——防抖与节流深入理解JavaScript——函数式编程深入理解JavaScript——垃圾集合机制深入理解JavaScript——数组深入理解JavaScript——循环来这里深入理解JavaScript——字符串