美文网首页
内存空间详细图解

内存空间详细图解

作者: 达文西_Huong | 来源:发表于2020-06-17 10:54 被阅读0次

    内存空间详细图解 【转载】

    原文:https://mp.weixin.qq.com/s/NGqdjhoU3MR9LD0yH6tKIw?


    变量对象与堆内存

        var a = 20;
        var b = 'abc'
        var c = true
        var d = {m:20}
    

    因为JavaScript 具有自动垃圾回收机制,所以对于前端开发来说,内存空间并不是一个经常会被提及的概念。但是这个概念其实对于js的一些基本概念的理解是十分有帮助的。下面就来详细的介绍一下。

    一、栈与堆

    注: 栈,也可以叫 堆栈

    js在严格意义上并没有区分栈内存和堆内存。因此我们可以粗略的理解为 <u>JavaScript的所有数据都保存在堆内存中。</u> 但是在某一些场合,我们仍然需要基于堆栈数据结构的思路进行处理,比如JavaScript的执行上下文。执行上下文在逻辑上实现了堆栈。因此理解堆栈结构和原理仍然十分重要

    要简单理解栈的存取方式,我们可以通过类比兵乓球盒子来分析。如下图

    image

    这种兵乓球的存放方式与栈中存取数据的方式如出一辙。处于盒子中最顶层的兵乓球5,它一定是最后被放出去,但可以最先被使用。而我们想要使用底层的兵乓球1,就必须将上面的4个兵乓球取出来,让兵乓球1处于盒子顶层。这就是栈空间先进后出,后进先出的特点。图中已经详细的表明了栈空间的存储原理。

    存取数据的方式,则与书架与书非常相似

    书虽然也整齐的存放在书架上,但是我们只要知道书的名字,我们就可以很方便的取出我们想要的书,而不用像从兵乓球盒子里取出兵乓一样,非得将上面的所有兵乓球拿出来才能渠道中间的某一个兵乓球。好比在JSON格式的数据中,我们存储的Key-Value是可以无序的,因为顺序的不同并不影响我们的使用,我们只需要关心书的名字。

    二、变量对象与基础数据类型

    JavaScript 的执行上下文生成之后,会创建一个叫做变量对象的特殊对象,JavaScript的基础数据类型往往都会保存在变量对象中

    严格意义上来说,变量对象也是存放在堆内存中,但是由于对象的特殊智能,我们在理解时仍然需要将其于堆内存区分开来

    基础数据类型都是一些简单的数据段,js中有5种数据类型。基础数据类型都是按值访问,因为我们可以直接操作保存在变量中的实际的值

    三、引用数据类型与堆内存

    与其他语言不同,js的引用数据类型,比如数组,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象JavaScript不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗略的理解为保存在变量对象中的一个地址,该地址与堆内存的实际值相关。

    为了更好的搞懂变量对象与堆内存,我们可以结合一下例子。

        var a1 = 0; // 变量对象
        var a2 = "this is string" // 变量对象
        var a3 = null // 变量对象
    
        var b = {m:20}   // 变量b存在于变量对象中,{m: 20} 作为对象存在于堆内存中
        var c = [1, 2, 3]; // 变量c存在于变量对象中,[1, 2, 3] 作为对象存在于堆内存中
    
    image

    因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量对象中获取了该对象的引用地址(或者地址指针),然后再从堆内存中取得我们需要的数据

    理解了JS的内存空间,我们就可以借助内存空间的特性来验证一下引用类型的一些特点

        var a = 20
        var b = a
        b = 30
    
        // 这时 a 的值为 20
    
        var m = { a:10, b:20 } 
        var n = m 
        n.a = 15;
    
        // 这时候m.a的值是15
    

    再变量对象中的数据发生复制行为时,系统会自动为新增的变量分配一个新值。 var b = a执行之后,a与b虽然值都等于20,但是它们其实已经是相互独立互不影响的值了。具体如图。所以我们修改了b的值以后,a的值并不会发生变化

    image

    在demo02 中,我们通过var n = m 执行一次复制引用类型的操作。引用类型的复制同样也会为新的变量自动分配一个新的值保存在变量对象中,但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,尽管它们相互独立,但是在变量对象中访问到具体对象实际上是同一个。如图所示

    因此当我改变n时,m也发生了变化。这就是所用类型的特性

    image

    通过内存的角度来理解,是不是感觉要轻松很多,除此之外,我们还可以以此为基础,一步一步的理解JavaScript的执行上下文,作用域链,闭包,原型链等重要概念。

    内存空间管理

    因为JavaScrip具有自动垃圾收集机制,所以我们在开发时好像不用关心内存的使用问题,内存的分配与回收都完全实现了自动管理。但是根据我们自己的开发经验,了解内存机制有助于我们写代码过程中的发生了什么。

    javascript 的内存生命周期

    1. 分配你所需要的内存
    2. 使用分配到的内存
    3. 不需要的时候将其释放,归还

    为了便于理解,我们使用一个简单的例子来解释这个周期

        var a = 20;     // 在内存中给数值分配空间
        alert(a + 100 ) // 使用内存
        a = null;       // 使用完毕之后,释放内存空间
    

    第一步和第二步我们都很好理解,javaScript在定义变量时就完成了内存分配。第三步释放内存空间则是我们需要重点理解的一个点。

    JavaScrip 有自动垃圾手机机制,那么这个自动垃圾收集机制的原理是什么呢?其实很简单,就是找出那些不再继续使用的值,然后释放其占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。

    在JavaScript中,最常用的是通过标记清除的算法来找到哪些对象是不再继续使用的,因此a = null其实仅仅只是做了一个释放引用的操作,让 a 原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。而在适当的时候解除引用,是为页面获得更好性能的一个重要方式。

    • 在局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。但是全局变量什么时候需要自动释放内存空间则很难判断,因此在我们的开发中,需要尽量避免使用全局变量,以确保性能问题。
    • 要详细了解垃圾收集机制,建议阅读《JavaScript高级编程》中的4.3节

    相关文章

      网友评论

          本文标题:内存空间详细图解

          本文链接:https://www.haomeiwen.com/subject/ywmjxktx.html