1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        JavaScript中變量存儲在堆中還是棧中?

        共 5404字,需瀏覽 11分鐘

         ·

        2021-09-14 16:27


        • 作者:滴滴 - 六耳
        • 來源:https://www.zhihu.com/question/482433315/answer/2083349992

        JavaScript中基本類型存儲在堆中還是棧中?

        ---- 不基本類型的基本類型

        看到這個問題,相信大家都覺得這個題目實在基礎的不能再基礎了。隨手百度一下,就能看到很多人說:基本類型存在棧中,引用類型存在堆中。

        真的這么簡單么?

        一、裝不進冰箱的大象

        讓我們看一下這段代碼:

        在這里,我們聲明了一個67MiB大小的字符串,如果字符串真的存在棧中,這就不好解釋了。畢竟,v8默認的棧區(qū)大小為984KiB??隙ㄊ谴娌幌碌?。

        注:在不同時期,不同操作系統(tǒng)中V8對于字符串大小的限制并不相同。大概有個范圍是256MiB ~ 1GiB
        node --v8-options | grep -B0 -A1 stack-size

        說到這里,各位是不是心里已經開始疑惑了呢。難道百度的答案不對,得用谷歌搜?

        讓我們看看這到底是怎么回事。

        二、影分身的字符串

        const BasicVarGen = function ({
            this.s1 = 'IAmString'
            this.s2 = 'IAmString'
        }


        let a = new BasicVarGen()
        let b = new BasicVarGen()

        在這里,我們聲明了兩個一樣的對象,每個對象包括兩個相同的字符串。

        通過開發(fā)者工具,我們看到雖然我們聲明了四個字符串,但是其內存指向了同一個地址。

        備注:chrome無法查看實際地址,此處為抽象后的地址

        這說明了啥?說明了四個字符串中存的是引用地址。

        所以上文中那個無法裝進冰箱的大象,也就好解釋了。字符串并沒有存到棧中,而是存到了一個別的地方,再把這個地方的地址存到了棧中。

        那,讓我們修改一下其中一個字符串的內容

        const BasicVarGen = function ({
            this.s0 = 'IAmString'
            this.s1 = 'IAmString'
        }


        let a = new BasicVarGen()
        let b = new BasicVarGen()
        debugger
        a.s0 = 'different string'
        a.s2 = 'IAmString'

        debugger之前的內存快照

        debugger之后的內存快照

        我們可以看到,a.s0 一開始內容為 ‘IAmString’ ,在我們修改其內容后,地址發(fā)生了變化。

        而我們新增的a.s2 其內容為 ‘IAmString’ ,其地址與其他值為 ‘IAmString’  的變量保持一致。

        當我們聲明一個字符串時:

        1. v8內部有一個名為stringTable的hashmap緩存了所有字符串,在V8閱讀我們的代碼,轉換抽象語法樹時,每遇到一個字符串,會根據(jù)其特征換算為一個hash值,插入到hashmap中。

          在之后如果遇到了hash值一致的字符串,會優(yōu)先從里面取出來進行比對,一致的話就不會生成新字符串類。

        2. 緩存字符串時,根據(jù)字符串不同采取不同hash方式。

        所以讓我們梳理一下,在我們創(chuàng)建字符串的時候,V8會先從內存中(哈希表)查找是否有已經創(chuàng)建的完全一致的字符串,如果存在,直接復用。如果不存在,則開辟一塊新的內存空間存進這個字符串,然后把地址賦到變量中。這也是為什么我們不能直接用下標的方式修改字符串: V8中的字符串都是不可變的。

        拿出一個js的基本類型拷貝舉例講一下v8的實現(xiàn)邏輯和常規(guī)的大家理解的邏輯(雅文)

        // 例: 
        var a = "劉瀟灑";   // V8讀取字符串后,去stringTable查找是否存在 不存在 hashTable 插入 '劉瀟灑' 并把'劉瀟灑'的引用存入 a
        var b = a; // 直接拷貝 '劉瀟灑' 的引用 
        b = "譚雅文"// 查找 無 存入stringTable

        疑問點:

        const BasicVarGen = function ({
            this.s0 = 'IAmString'
            this.s1 = 'IAmString'
        }


        let a = new BasicVarGen()
        let b = new BasicVarGen()
        debugger
        a.s0 = 'different string'
        a.s2 = 'IAmString'




        a.s3 = a.s2+a.s0;   // 疑問點: 字符串拼接做了哪些操作?
        a.s4 = a.s2+a.s

        同時申請兩個拼接的字符串,內容相同。

        可以看到,雖然其內容相同。但是地址并不相同。而且,地址前方的Map描述也發(fā)生了變化。

        字符串拼接時如果以傳統(tǒng)方式(如 SeqString)存儲,拼接操作的時間復雜度為 O(n) ,采用 繩索結構[Rope Structure] (也就是 ConsString 所采用的數(shù)據(jù)結構)可以減少拼接所花費的時間。

        如果字符串是這樣,那別的基本類型也是如此么?

        三、如朕親臨的 ‘奇球’

        說完字符串,讓我們看看V8中另外一類典型的‘基本類型’:oddBall。

        拓展自oddBall的type

        讓我們再做一個小實驗:

        我們可以看到 上圖中列舉的基本類型,地址也是相同的。在賦值時,也是就地復用。(而且這些拓展自oddBall的基本類型,其地址是固定的,也就是說,在V8跑起來的第一時間,不管我們有沒有聲明這些基本類型,他們都已經被創(chuàng)建完畢了。而我們聲明對象時,賦的是他們的引用。這也可以解釋為什么我們說基本類型是賦到棧中:在V8中,存放在 @73 的值,永遠是空字符串,那么v8就可以等效把這些地址視為值本身。)

        讓我們看看源碼,驗證一下:

        生成各種oddBall類型的方法,可以看出返回的是一個地址

        undefined賦值給一個變量,其實賦的是地址

        getRoot方法

        偏移量定義的地方

        四、撲朔迷離的數(shù)字

        之所以叫撲朔迷離的數(shù)字,是因為還沒有搞明白其分配與改變時內存分配的機制。(其內存是動態(tài)的)

        不過我們可以參考一篇文章。

        數(shù)字在V8中分為 smi 和 heapNumber。

        smi 直接存進內存范圍為 :-231 到 231-1(231≈2*10?)的整數(shù)

        heapNumber 類似字符串 不可變范圍為 :所有非smi的數(shù)字

        最低位用來表示是否為指針 最低位為1 則是一個指針

        const o = {
          x42,  // Smi
          y4.2// HeapNumber
        };

        o.x中的42會被當成Smi直接存儲在對象本身,而o.y中的4.2需要額外開辟一個內存實體存放,并將o.y的對象指針指向該內存實體。

        如果是 32 位操作系統(tǒng),用32位表示smi 可以理解,可是64位操作系統(tǒng)中,為什么 smi 范圍也是 -231 到 231-1(231≈2*10?)?

        ECMAScript 標準約定number數(shù)字需要被當成 64 位雙精度浮點數(shù)處理,但事實上,一直使用 64 位去存儲任何數(shù)字實際是非常低效的(空間低效,計算時間低效 smi大量使用位運算),所以 JavaScript 引擎并不總會使用 64 位去存儲數(shù)字,引擎在內部可以采用其他內存表示方式(如 32 位),只要保證數(shù)字外部所有能被監(jiān)測到的特性對齊 64 位的表現(xiàn)就行。

        const cycleLimit = 50000
        console.time('heapNumber')
        const foo = { x1.1 };
        for (let i = 0; i < cycleLimit; ++i) {
        // 創(chuàng)建了多出來的heapNumber實例
          foo.x += 1
        }
        console.timeEnd('heapNumber'// slow   


        console.time('smi')
        const bar = { x1.0 };
        for (let i = 0; i < cycleLimit; ++i) {
          bar.x += 1;
        }
        console.timeEnd('smi')  // fast

        疑問點:

        const BasicVarGen = function ({


            this.smi1 = 1
            this.smi2 = 2
            this.heapNumber1 = 1.1
            this.heapNumber2 = 2.1
        }
            let foo = new BasicVarGen()
            let bar = new BasicVarGen()
            
            debugger
            
            baz.obj1.heapNumber1 ++



        在數(shù)字中,一個數(shù)字的值都沒有修改,其他的數(shù)字地址也都變了。

        五、小結:基本類型到底存在哪里?

        字符串:   存在堆里,棧中為引用地址,如果存在相同字符串,則引用地址相同。

        數(shù)字:      小整數(shù)存在棧中,其他類型存在堆中。

        其他類型:引擎初始化時分配唯一地址,棧中的變量存的是唯一的引用。

        這里只能算是大概講明白了基本類型存在哪里,

        在學習探索的過程中,雖然一些疑問得到了解答,但是問題卻變得更多了。

        希望下次繼續(xù)分享。(點贊超一百繼續(xù)更新)

        本文V8代碼 commitId:dc81345f7e29b4fb6fbb83aa6374ec835e95b2c9

        參考文章:

        1. The story of a V8 performance cliff in React · V8

        2. https://v8.dev/blog/react-cliff

        3. 用JavaScript帶你體驗V8引擎解析字符串 - 書生小龍 - 博客園 (cnblogs.com)

        4. https://www.cnblogs.com/QH-Jimmy/p/11160712.html

        瀏覽 39
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一级电影网 | 好爽…又高潮了毛片喷水 | 老丨熟女| 男人操女人比 | 无尽3d女同裸体 欧美色图视频一区 | 中文字幕 欧美 日韩 | 娇妻在粗大的胯下受辱 | 五月天婷婷色网 | 国免产女人18毛片水真多1 | 免费成人毛片 |