堆栈空间和垃圾回收机制

2020-03-19  本文已影响0人  Lyan_2ab3

js 是什么类型的语言

我们把这种在使用之前就需要确认其变量数据类型的称为静态语言。
我们把在运行过程中需要检查数据类型的语言称为动态语言
JavaScript 就是动态语言,因为在声明变量之前并不需要确认其数据类型

JavaScript 是一种弱类型的、动态的语言

补充:
JS 基本类型:

JS 的数据类型有几种?

8种:Number、String、Boolean、Null、undefined、object、symbol、bigInt。

Object 中包含了哪几种类型?

包含了Data、function、Array等。这三种是常规用的。

JS的基本类型和引用类型有哪些呢?

基本类型(单类型):除Object。 String、Number、boolean、null、undefined
引用类型:object。里面包含的 function、Array、Date。

那么讲一下 基本数据类型和引用类型 存储数据有什么不同?

堆栈空间

栈空间就是我们之前反复提及的调用栈,是用来存储执行上下文的

function foo(){
    var a = 1
    var b = a
    a = 2
    console.log(a) // 2
    console.log(b) // 1
}
foo()

function foo(){
    var a = {name:"极客时间"}
    var b = a
    a.name = "极客邦" 
    console.log(a)  // {name:"极客邦"}
    console.log(b) // {name:"极客邦"}
}
foo()

从上面代码中我们可以看到第一段代码 a 改变 b 不会改变,第二段代码 a 改变 b 也改变了, 我们前面介绍了 js 的数据类型。
可以知道,第一段代码中a 和 b 都是 基本的数据类型。
第二段中a 和b 是引用类型
那么我们下面看下 基本类型和引用类型是如何存储的数据


function foo(){
    var a = "极客时间"
    var b = a
    var c = {name:"极客时间"}
    var d = c
}
foo()

执行引用类型的调用栈.png 对象类型堆栈.png

对象类型是存放在堆空间的,在栈空间中只是保留了对象的引用地址,
原始类型的数据值都是直接保存在“栈”中的,引用类型的值是存放在“堆”中的

那么为什么要分两个空间来存储呢?

调用栈切换上下文状态.png

栈空间都不会设置太大,主要用来存放一些原始类型的小数据
堆空间很大,能存放很多大的数据,不过缺点是分配内存和回收内存都会占用一定的时间。

所以回到前面的题,原始类型的赋值会完整复制变量值,而引用类型的赋值是复制引用地址。

d=c的操作就是把 c 的引用地址赋值给 d

引用类型赋值.png

闭包如何产生?


function foo() {
    var myName = "极客时间"
    let test1 = 1
    const test2 = 2
    var innerBar = { 
        setName:function(newName){
            myName = newName
        },
        getName:function(){
            console.log(test1)
            return myName
        }
    }
    return innerBar
}
var bar = foo()
bar.setName("极客邦")
bar.getName()
console.log(bar.getName())

调用栈中的数据是如何回收的


function foo(){
    var a = 1
    var b = {name:"极客邦"}
    function showName(){
      var c = "极客时间"
      var d = {name:"极客时间"}
    }
    showName()
}
foo()

内存模型.png 从栈中回收 showName 执行上下文.png

堆中的数据是如何回收的

上面那段代码的 foo 函数执行结束之后,ESP 应该是指向全局执行上下文的,那这样的话,showName 函数和 foo 函数的执行上下文就处于无效状态了,不过保存在堆中的两个对象依然占用着空间

foo 函数执行结束后的内存状态.png

要回收堆中的垃圾数据,就需要用到 JavaScript 中的垃圾回收器了。

在 V8 中会把堆分为新生代和老生代两个区域,
新生代中存放的是生存时间短的对象,
老生代中存放的生存时间久的对象。

副垃圾回收器,主要负责新生代的垃圾回收
主垃圾回收器,主要负责老生代的垃圾回收。

回收流程:

标记空间中活动对象和非活动对象 ---> 回收非活动对象所占据的内存 ---->做内存整理。

副垃圾回收器:

副垃圾回收器主要负责新生区的垃圾回收。而通常情况下,大多数小的对象都会被分配到新生区,所以说这个区域虽然不大,但是垃圾回收还是比较频繁的

v8堆.png

为了执行效率,一般新生区的空间会被设置得比较小
所以很容易被存活的对象装满整个区域。所以JavaScript 引擎采用了对象晋升策略,也就是经过两次垃圾回收依然还存活的对象,会被移动到老生区中。

主垃圾回收器:

主垃圾回收器是采用标记 - 清除(Mark-Sweep)的算法进行垃圾回收的

标记过程.png
上一篇 下一篇

猜你喜欢

热点阅读