js內存數據

基本儲存單元

位(bit):二進制數中的一個數位,能夠是0或者1,是計算機中數據的最小單位。node

字節(Byte,B):計算機中數據的基本單位,每8位組成一個字節。各類信息在計算機中存儲、處理至少須要一個字節。例如,一個ASCII碼用一個字節表示,一個漢字用兩個字節表示。c++

字(Word):兩個字節稱爲一個字。漢字的存儲單位都是一個字。編程

擴展的存儲單位

在計算機各類存儲介質(例如內存、硬盤、光盤等)的存儲容量表示中,用戶所接觸到的存儲單位不是位、字節和字,而是KB、MB、GB等,但這不是新的存儲單位,而是基於字節換算的。數組

KB:1KB=1024B
MB:1MB=1024KB
GB:1GB=1024MB
TB:1TB=1024GB

UTF-8編碼:一個英文字符等於一個字節,一箇中文(含繁體)等於三個字節。中文標點佔三個字節,英文標點佔一個字節。瀏覽器

Unicode編碼:一個英文等於兩個字節,一箇中文(含繁體)等於兩個字節。中文標點佔兩個字節,英文標點佔兩個字節安全

數組

數組最大長度 2^32 – 1(即4294967296 – 1),js數組並非真正意義上的數組(棧)數據結構

在計算機科學中,數組數據結構(英語:array data structure),簡稱數組(英語:Array),是由相同類型的元素(element)的集合所組成的數據結構,分配一塊連續的內存來存儲。性能

JavaScript的數組是否分配連續內存取決於數組成員的類型,若是統一是單一類型的數組那麼會分配連續內存,若是數組內包括了各類各樣的不一樣類型,那麼則是非連續內存。優化

非連續內存的數組用的是相似哈希映射的方式存在,它能夠經過多種數據結構實現,其中一種是鏈表。好比聲明瞭一個數組,他被分配給了100一、20十一、108八、1077四個非連續的內存地址,經過指針鏈接起來造成一個線性結構,那麼當咱們查詢某元素的時候實際上是須要遍歷這個線性鏈表結構的,這十分消耗性能。編碼

如今, JavaScript 引擎已經在爲同種數據類型的數組分配連續的存儲空間了。另外,在 ES2015/ES6 中, 數組還有其它改進。 TC39 決定在 JavaScript 中引入類型化數組,因此現在咱們有 ArrayBuffer了。ArrayBuffer 會有一大塊連續的存儲位置,你能用它作任何你想作的事情。不過,直接處理內存涉及很是底層的操做,至關複雜。

字符串

字符串理論上的最大長度是沒有限制的,只要你的js解釋器內存夠大,可是當前大多數瀏覽器都不能支持2^28(即268435456)位字符串。若是所有存儲英文字符,則大小爲268435456 / 1024 / 1024 = 256 MB

那麼這麼大的字符串在內存中是怎麼存儲的呢?
ECMAScript 5.1規範在「內存」的定義上很是模糊,並無明肯定義出ECMAScript實現中的各個運行時區域的劃分,也就不存在「stack/heap劃分」。因此,從定義層面看不出String存儲的字符串是在stack仍是heap上分配的。

ECMAScript規範只規定了String類型要是一個值類型,這個類型要能夠存儲UTF-16爲單元的字符,徹底沒有規定String類型要如何實現。因此各個JavaScript引擎的具體實現就各顯神通了。

V8在內存分配上面,對String直接使用堆存儲,不用通過c++堆外分配內存,而且Google也對String進行優化,在實際的拼接測速對比中,String比Buffer快。

在 V8 中字符串有以下 5 種表達模式:
SeqString
已經要查看內容的字符串:使用flat string思路來實現,本質上說就是用數組形式來存儲String的內容。
實際數據存儲時分爲 OneByte、TwoByte(Unicode)兩類。

ConsString
拼接字符串但還沒有查看其內容:在字符串拼接時,採用樹形結構表達拼接後(first + second)的字符串。使用「rope」思路或其它延遲拼接的思路來實現。當須要查看其內容時則進行「扁平化」操做將其轉換爲flat string表現形式。最多見rope的內部節點就像二叉樹同樣,但也能夠有采用更多叉樹的設計的節點,或者是用更動態的多叉樹實現。

SliceString(parent, offset)
子串(substring):在字符串切割時,採用 offset 與 [length] 表達父字符串(parent)的一部分。使用「slice」思路來實現,也就是說它只是一個view,本身並不存儲字符內容而只是記錄個offset和length,底下的存儲共享自其引用的源字符串。

ThinString(actual)
值得駐留(intern)的字符串:在有些場景下會重複出現的字符串,當兩個變量保存相同的字符串時,它們其實是保存了這個字符串在內存中的地址。最大的好處是在特殊場景下有些字符串會常常重複出現,或者要常常用於相等性比較,把這些字符串駐留起來能夠節省內存(內容相同的字符串只駐留一份),而且後續使用可使用指針比較來代替徹底的相等性比較(由於駐留的時候已經比較過了)。在多數狀況下能夠被認爲與 ConsString(actual, empty_string) 等價。

ExternalString
外來字符串:表明了產生在 V8 堆外的字符串資源。有時候JavaScript引擎跟外界交互,外界想直接把一個char8_t或者char16_t傳給JavaScript引擎看成JavaScript字符串用。JavaScript引擎可能會針對某些特殊場景提供一種包裝方式來直接把這些外部傳進來的字符串看成JavaScript String,而不拷貝其內容。

值得注意的是:雖然ECMAScript的String值是值類型的,這並不就是說「String值就是在棧上的」。正好相反,V8所實現的String值所有都是在V8的GC堆上存儲的,傳遞String值時實際上傳遞的是指向它的指針。但因爲JavaScript的String值是不可變的,因此底層實現不管是真的把String「放在棧上」仍是傳遞指針,對上層應用的JavaScript代碼而言都沒有區別。

ExternalString雖然特殊但也不例外:它實際存儲字符串內容的空間雖然是從外部傳進來的,不在V8的GC堆裏,可是ExternalString對象自身做爲一個對象頭仍是在GC堆裏的,因此該String類型實現邏輯上說仍是在GC堆裏。

v8中,Boolean、Null、Undefined、Number保存在棧內存中,這些類型的值均可以用32位的數據來表示。那麼剩下的數據類型Symbol、BigInt、Object、String就都是要保存在堆中的,棧裏面只會保存這些值的地址的引用。

棧內存:

1.存儲的值大小固定
2.空間較小
3.能夠直接操做其保存的變量,運行效率高
4.由系統自動分配存儲空間

棧內存是一個線性的、規則的、大小基本固定的、有序的排列起來的一塊塊內存空間,每一個單元大小固定,規則有序的排列下來

堆內存:

5.存儲的值大小不定,可動態調整
6.空間較大,運行效率低
7.沒法直接操做其內部存儲,使用引用地址讀取
8.經過代碼進行分配空間

數字

Number,遵循 IEEE 754 規範,採用雙精度存儲(double precision),佔用 64 位,其中1位用來表示符號位,11位用來表示指數,剩下52位表示尾數,就是這個Number.MAX_SAFE_INTEGER,又稱爲最大安全數,其值爲9007199254740991,換算成二進制就是2^53-1,即佔52位。。大於 9007199254740992 的可能會丟失精度

9007199254740992 + 1 // 丟失
9007199254740992 + 2 // 未丟失
9007199254740992 + 3 // 丟失
9007199254740992 + 4 // 未丟失

相似的,數字還有一個最小安全數Number.MIN_SAFE_INTEGER,其值爲-9007199254740991

數字最大數爲Number.MAX_VALUE,其值爲1.7976931348623157e+308,介於2^1023 – 2^1024之間。大於的值MAX_VALUE表示爲Infinity

ECMAScript 標準約定number數字須要被當成 64 位雙精度浮點數處理,但事實上,一直使用 64 位去存儲任何數字實際是很是低效的,因此 JavaScript 引擎並不總會使用 64 位去存儲數字,引擎在內部採用其餘內存表示方式(如 32 位),只要保證數字外部全部能被監測到的特性對齊 64 位的表現就行。

V8不只僅是使用32位來表示數字那麼簡單,還對數字進行了分類,將數字分爲了Smi 和 HeapNumber(這個僅僅是引擎層面的處理,js內部只認識數字,不區分整數和浮點數)。

V8中Smi表明的是小整數(-2^31,2^31-1),而HeapNumber則表明了一些浮點數以及沒法用32位表示的數,好比NaN,Infinity,-0等。由於小整數在咱們的編碼過程當中太常見了,因此,V8專門把它拿出來,而且對其進行了優化操做,這樣它就能夠進行快速整型操做,好比for循環等。

當咱們更新他們的值的時候,Smi的值會原地更新,而HeapNumber因爲它不可變的特性,V8會開闢一個新的內存實體用來儲存新的值,若是咱們須要頻繁更新HeapNumber的值,執行效率會比Smi慢得多。

v8引擎與內存分配

V8 是谷歌開發的高性能 JavaScript 引擎,該引擎使用 C++ 開發。目前主要應用在 Google Chrome 瀏覽器和 node.js 當中。

V8 自帶的高性能垃圾回收機制,使開發者可以專一於程序開發中,極大的提升開發者的編程效率。可是方便之餘,也會出現一些對新手來講比較棘手的問題:進程內存暴漲,cpu 飆升,性能不好等。

一個 V8 進程的內存一般由如下幾個塊構成:

新生代內存區(new space)

64位下新生代的空間爲64M,32位下新生代爲16M。大多數的對象都會被分配在這裏,這個區域很小可是垃圾回收比較頻繁;

老生代內存區(old space)

64位下老生代爲1400M;32位下老生代爲700M。屬於老生代,這裏只保存原始數據對象,這些對象沒有指向其餘對象的指針;

大對象區(large object space)

這裏存放體積超越其餘區大小的對象,每一個對象有本身的內存,垃圾回收其不會移動大對象區;

代碼區(code space)

代碼對象,會被分配在這裏。惟一擁有執行權限的內存;

map 區(map space)

存放 Cell 和 Map,每一個區域都是存放相同大小的元素,結構簡單。

相關文章
相關標籤/搜索