成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JS 中變量存儲在堆中還是棧中?(深入內(nèi)存原理)

開發(fā) 前端
看到這個(gè)問題,相信大家都覺得這個(gè)題目實(shí)在基礎(chǔ)的不能再基礎(chǔ)了。隨手百度一下,就能看到很多人說:基本類型存在棧中,引用類型存在堆中。

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

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

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

真的這么簡單么?

一、裝不進(jìn)冰箱的大象

讓我們看一下這段代碼:

在這里,我們聲明了一個(gè)67MiB大小的字符串,如果字符串真的存在棧中,這就不好解釋了。畢竟,v8默認(rèn)的棧區(qū)大小為984KiB。肯定是存不下的。

 注:在不同時(shí)期,不同操作系統(tǒng)中V8對于字符串大小的限制并不相同。大概有個(gè)范圍是256MiB ~ 1GiB 

 

  1. node --v8-options | grep -B0 -A1 stack-size 

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

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

二、影分身的字符串 

  1. const BasicVarGen = function () {  
  2.     this.s1 = 'IAmString'  
  3.     this.s2 = 'IAmString'  
  4.  
  5. let a = new BasicVarGen()  
  6. let b = new BasicVarGen() 

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

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

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

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

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

那,讓我們修改一下其中一個(gè)字符串的內(nèi)容 

  1. const BasicVarGen = function () {  
  2.     this.s0 = 'IAmString'  
  3.     this.s1 = 'IAmString'  
  4.  
  5. let a = new BasicVarGen()  
  6. let b = new BasicVarGen()  
  7. debugger  
  8. a.s0 = 'different string'  
  9. a.s2 = 'IAmString' 

debugger之前的內(nèi)存快照

debugger之后的內(nèi)存快照

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

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

當(dāng)我們聲明一個(gè)字符串時(shí):

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

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

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

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

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

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

疑問點(diǎn): 

  1. const BasicVarGen = function () {  
  2.     this.s0 = 'IAmString'  
  3.     this.s1 = 'IAmString'  
  4.  
  5. let a = new BasicVarGen()  
  6. let b = new BasicVarGen()  
  7. debugger  
  8. a.s0 = 'different string'  
  9. a.s2 = 'IAmString'  
  10. aa.s3 = a.s2+a.s0;   // 疑問點(diǎn): 字符串拼接做了哪些操作?  
  11. aa.s4 = a.s2+a.s 

同時(shí)申請兩個(gè)拼接的字符串,內(nèi)容相同。

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

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

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

三、如朕親臨的 ‘奇球’

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

拓展自oddBall的type

讓我們再做一個(gè)小實(shí)驗(yàn):

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

讓我們看看源碼,驗(yàn)證一下:

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

undefined賦值給一個(gè)變量,其實(shí)賦的是地址

getRoot方法

偏移量定義的地方

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

之所以叫撲朔迷離的數(shù)字,是因?yàn)檫€沒有搞明白其分配與改變時(shí)內(nèi)存分配的機(jī)制。(其內(nèi)存是動態(tài)的)

smi 直接存進(jìn)內(nèi)存范圍為 :-2³¹ 到 2³¹-1(2³¹≈2*10⁹)的整數(shù)

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

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

  1. const o = {  
  2.   x: 42,  // Smi  
  3.   y: 4.2, // HeapNumber  
  4. }; 

o.x中的42會被當(dāng)成Smi直接存儲在對象本身,而o.y中的4.2需要額外開辟一個(gè)內(nèi)存實(shí)體存放,并將o.y的對象指針指向該內(nèi)存實(shí)體。

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

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

  1. const cycleLimit = 50000  
  2. console.time('heapNumber')  
  3. const foo = { x: 1.1 };  
  4. for (let i = 0; i < cycleLimit; ++i) {  
  5. // 創(chuàng)建了多出來的heapNumber實(shí)例  
  6.   foo.x += 1;   
  7.  
  8. console.timeEnd('heapNumber') // slow    
  9. console.time('smi')  
  10. const bar = { x: 1.0 };  
  11. for (let i = 0; i < cycleLimit; ++i) {  
  12.   bar.x += 1;  
  13.  
  14. console.timeEnd('smi')  // fast 

疑問點(diǎn): 

  1. const BasicVarGen = function () {  
  2.     this.smi1 = 1  
  3.     this.smi2 = 2  
  4.     this.heapNumber1 = 1.1  
  5.     this.heapNumber2 = 2.1  
  6.  
  7.     let foo = new BasicVarGen()  
  8.     let bar = new BasicVarGen()    
  9.      debugger      
  10.     baz.obj1.heapNumber1 ++ 

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

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

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

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

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

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

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

 

責(zé)任編輯:龐桂玉 來源: 前端大全
相關(guān)推薦

2009-06-03 15:52:34

堆內(nèi)存棧內(nèi)存Java內(nèi)存分配

2009-06-08 22:01:03

Java堆Java棧區(qū)別

2010-10-14 09:34:34

JVM局部變量

2024-04-30 08:38:31

C++

2011-07-22 16:50:05

JAVA

2016-08-31 15:50:50

PythonThreadLocal變量

2018-06-20 10:34:56

堆棧iOSswift

2024-01-09 09:23:12

指針C++

2013-06-20 10:25:56

2022-03-16 08:39:19

StackHeap內(nèi)存

2011-07-22 17:06:22

java

2021-03-06 22:41:06

內(nèi)核源碼CAS

2012-08-15 14:44:53

GC

2017-03-19 16:40:28

漏洞Node.js內(nèi)存泄漏

2017-03-20 13:43:51

Node.js內(nèi)存泄漏

2020-05-22 10:40:33

ContinuatioJS前端

2018-07-09 13:40:24

前端javascript

2010-03-29 13:19:10

OracleMulti

2009-07-14 17:30:21

Jython語法學(xué)習(xí)

2016-08-31 15:41:19

PythonThreadLoca變量
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 久久久久久久久国产成人免费 | 麻豆changesxxx国产 | 亚洲日产精品 | 欧美视频一区二区三区 | 日操操夜操操 | 99re66在线观看精品热 | 精品成人在线视频 | 久草中文在线观看 | 亚洲一区精品在线 | 国产欧美日韩精品一区 | 日本成人免费观看 | 在线看一区二区 | 国产免费观看久久黄av片涩av | 欧美日韩亚洲国产 | 人人干超碰 | 国产欧美日韩二区 | 日韩在线精品视频 | 日本国产精品视频 | 成人在线视频免费播放 | 国产乱码精品一区二区三区忘忧草 | 伊人狠狠操 | 欧美日韩国产在线观看 | 99热国产免费 | 超碰人人人 | 久久国内精品 | 国产在线二区 | 久久精品日 | 国产精品久久久久永久免费观看 | 久久一级 | 精品一区国产 | 亚洲激情网站 | 久久网一区二区三区 | 国产精品一区一区 | 一级做a爰片性色毛片16 | 色综合天天天天做夜夜夜夜做 | 91爱爱·com | h视频免费在线观看 | 中国一级特黄毛片大片 | 国产999精品久久久久久 | 免费h在线 | 黄色大片在线免费观看 |