「字串」存在「棧記憶體」中?那我林三心可要槓你了哦!

Sunshine_Lin發表於2022-01-24

前言

大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。

在我們的認知裡:基礎型別存棧記憶體,引用資料型別存堆記憶體。

const a = '林三心'
const b = {
    age: 18,
    height: 180
}

超長字串

大家都知道, 字串 屬於 基礎型別 ,所以大家會覺得 字串 是存在 棧記憶體 中的,但是大家要知道,V8預設棧記憶體是 984Kib ,那如果一個 超長字串 > 984Kib 能裝的進 棧記憶體 嗎?這也就是一個比較經典的問題——大象裝箱問題,試問:一頭大象能裝進一個小箱子裡嗎?

截圖2022-01-17 下午11.13.35.png

一探究竟

堆快照

先來看一段程式碼

const func = function() {
  this.str1 = '林三心'
  this.str2 = 'Sunshine_Lin'
}

const a = new func()
const b = new func()

然後我們們來看看 堆快照 的詳情

WeChat9db795005f91b874bb0097857cb6f7a3.png

上面的結果可以看出:

  • a 和 b的 str1 都指向同一個地址
  • a 和 b的 str2 都指向同一個地址

那我們可不可以猜測出一個結論:字串的內容存於堆記憶體中,指標存於棧記憶體中,且相同的字串指向同一個堆記憶體地址

修改和新增字串

我們稍微修改下程式碼

const func = function() {
  this.str1 = '林三心'
  this.str2 = 'Sunshine_Lin'
}

const a = new func()
const b = new func()

// 修改str1
a.str1 = '哈哈哈哈哈哈哈哈哈哈'
// 新增str3,跟str2一樣
a.str3 = 'Sunshine_Lin'

再來看看現階段的 堆快照 的詳情

WeChat40be5ddc2bb160cdba3258fc98825136.png

上面的結果可以看出:

  • str1 修改成一個新的字串後,重新開闢了一個記憶體空間(新地址)
  • str3 新增之後,指標指向已有的 Sunshine_Lin 的記憶體空間

那我們可不可以猜測出一個結論:新增或者修改字串後,如果是一個之前不存在的字串,則新開闢記憶體空間,如果是已有的,則直接使用已有的記憶體空間

原始碼分析

當我們宣告一個字串時:

  • 1、v8內部有一個名為 stringTable hashmap 快取了所有字串,在V8閱讀我們的程式碼,轉換抽象語法樹時,每遇到一個字串,會根據其特徵換算為一個 hash值 ,插入到 hashmap 中。在之後如果遇到了 hash值 一致的字串,會優先從裡面取出來進行比對,一致的話就不會生成新字串類。
  • 2、快取字串時,根據字串不同採取不同 hash 方式。

截圖2022-01-17 下午11.13.42.png

原始碼

image.png

image.png

通俗易懂總結

當我們新建一個字串時,V8會從記憶體中查詢一下是否已經有存在的一樣的字串,找到的話直接複用。如果找不到的話,則開闢一塊新的記憶體空間來存這個字串,並把地址賦給變數。

大家有沒有想過,為什麼字串不能通過下標索引來進行修改呢?因為字串的修改本質上只能是通過整個的修改,而不能區域性修改。

結語

我是林三心,一個熱心的前端菜鳥程式設計師。如果你上進,喜歡前端,想學習前端,那我們們可以交朋友,一起摸魚哈哈,摸魚群,加我請備註【思否】

image.png

相關文章