语言类型
- 在使用前需要确认其变量数据类型的称为静态语言©,运行过程中需要检查数据类型的语言称为动态语言(js)。
- 在赋值时,语言回进程隐式类型转换,这类语言被称为弱类型语言,不支持隐式类型转换额语言称为强类型语言。
- JS数据类型
- JS是一种弱类型的、动态的语言:弱类型即不需要声明变量类型JS引擎在运行代码的时候会自己计算出来,动态即可以使用一个变量保存不同类型的数据。
- 原始类型:Boolean、Null、Undefined、Number、Sybmol、String、BigInt。
- 引用类型:Object。
- JS内存空间
- JS执行过程中主要有三种类型内存空间,分别是代码空间、栈空间和堆空间。
- 栈空间:调用栈空间,用来存储执行上下文的。
- 堆空间:js引擎判断出变量类型为引用类型,这个时候JS引擎不是直接将该对象放到变量环境中,二十将它分配到堆空间,分配后该对象会有一个在堆中的地址,然后在将该数据的地址写进引用变量的变量值中。
- 不将数据都放入栈空间是因为JS引擎需要栈来维护程序执行期间上下文的状态,提高上下文切换的效率,从而提高整个程序的执行效率。
- JS原始类型的数据值直接保存在栈中,引用类型的值保存在堆中。
- 堆空间很大,能存放很多大的数据,不过缺点是分配内存和回收内存都会占用一定的时间。
- 闭包
- 闭包产的核心有2部:第一步是需要预扫描内部函数;第二步是把内部函数引用的外部变量保存在堆中。
垃圾回收机制
- 数据被使用之后,之后不再需要了被称为垃圾数据。如果垃圾数据一直保存在内存中,那么内存会越用越多,所以堆垃圾数据进行回收,可以释放游下的内存数据空间。
- 不用语言的垃圾回收策略各不相同,通常氛围手动回收和自动回收。
- C/C++采用的是手动回收策略:何时分配内存、何时销毁内存都由代码控制,可以调用mallco函数来分配内存,free函数来释放内存,然后分配内存后,当数据不需要时,代码没有主动调用free函数来销毁,这种情况被称为内存泄漏。
- 自动回收策略:产生的垃圾数据是由垃圾回收其来释放的。
- JS调用栈中的数据是如何回收的?
- 调用栈中记录当前执行状态的指针(称为ESP),指针下移操作就是销毁对应函数执行上下文的过程。同时该函数在调用栈中为无效内存,下一个入栈的函数会覆盖掉改块内存空间。
- JS堆中的数据是如何回收的?
- 要回收堆中的垃圾数据,就需要用到JS中的垃圾回收器了。
- 代际假说:特点一对象已经分配内存,很快就变得不可访问,特点二:不死的对象,会活的很久。这2特点适合大多数的动态语言。
- V8会把堆分为新生代和老生代两个区域,新生代存放的是生存时间短的对象,老生代中存放的生存时间久的对象。
- 新生区:通常只支持1-8M容量,使用副垃圾回收器回收垃圾数据。
- 老生区:存放得容量较大,使用主垃圾回收器回收垃圾数据。
- 垃圾回收器的工作流程
- 标记空间中活动对象和非活动对象。所谓活动对象就是在使用对象,非活动对象就是可以进行垃圾回收的对象。
- 回收非活动对象所占据的内存。在所有的标记完成后,统一清理内存中被标记为可回收的对象。
- 内存整理:频繁回收对象后,内存中会存在大量不连续空间,我们把这些不连续的内存空间称为内存碎片,当内存中出现大量的内存碎片后,如果需要分配较大连续内存的时候肯出现内存不足的情况。
- 副垃圾回收器
- 采用Scanvenge算法:把新生代空间对半划分为2个区域,一半是对象区域,一半是空闲区域,
- 新加入的对象会存放在对象区域,当对象区域快被写满时,就需要执行一次垃圾清理操作。
- 垃圾清理过程中,首先需要对对象区域中的垃圾做标记,标记完成后,就叫你如垃圾清理阶段。
- 副垃圾回收器会把这些才能活的对象复制到空闲区域,同时它还会把这些对象有序地排列起来,这个过程久称为内存整理操作,复制后空闲区域就没有内存碎片了。
- 完成复制后,对象区域和空闲区域进行角色反转,这样就完成了垃圾对象的回收操作了,同时这种角色翻转的操作还能让新生代中的这2块区域无限重复使用下去。
- 复制操作需要时间成本,为了执行效率,一般新生区的空间会被设置的比较小,所以很容易被存活的对象装满整个新生区域。为此JS引擎采用对象晋升策略,经过2此垃圾回收依然还存活的对象,会被移动到老生区中。
- 主垃圾回收器
- 除了新生区中晋升的对象,一些大的对象会直接分配到老生区,因此老生区有2个特点,对象占用空间大,对象存活时间长。
- 采用标记-清除(Mark-Sweep)的算法进行垃圾回收。
- 首先标记过程阶段:从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素可以判断为垃圾数据。
- 垃圾清除过程:这一过程会参数大量不连续的内存碎片,当碎片过多会导致大对象无法分配到足够的连续的内存,为此产生了另一种算法"标记-整理(Mark-Compact)",后续不走不是直接对可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
- 全停顿
- JS时运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的JS脚本暂停下来,待垃圾回收完毕后在恢复脚本执行,这种行为被称为全停顿。
- 为了降低老生代的垃圾回收而造成的卡顿,V8将标记过程氛围一个个的子标记过程同时让垃圾回收标记和JS应用逻辑交替进行,直到标记阶段完成,这个算法被称为”增量标记(Incremental Marking)“算法。