「字符串」存在「棧內存」?那我可要杠你了哦!
前言
大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心。
在我們的認知里:基礎類型存棧內存,引用數據類型存堆內存。
const a = '林三心'
const b = {
age: 18,
height: 180
}
圖片
超長字符串
大家都知道,字符串屬于基礎類型,所以大家會覺得字符串是存在棧內存中的,但是大家要知道,V8默認棧內存是984Kib,那如果一個超長字符串 > 984Kib能裝的進棧內存嗎?這也就是一個比較經典的問題——大象裝箱問題,試問:一頭大象能裝進一個小箱子里嗎?
圖片
一探究竟
堆快照
先來看一段代碼
const func = function() {
this.str1 = '林三心'
this.str2 = 'Sunshine_Lin'
}
const a = new func()
const b = new func()
然后咱們來看看堆快照的詳情
圖片
上面的結果可以看出:
- 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'
再來看看現階段的堆快照的詳情
圖片
上面的結果可以看出:
- str1修改成一個新的字符串后,重新開辟了一個內存空間(新地址)
- str3新增之后,指針指向已有的Sunshine_Lin的內存空間
那我們可不可以猜測出一個結論:新增或者修改字符串后,如果是一個之前不存在的字符串,則新開辟內存空間,如果是已有的,則直接使用已有的內存空間
源碼分析
當我們聲明一個字符串時:
- 1、v8內部有一個名為stringTable的hashmap緩存了所有字符串,在V8閱讀我們的代碼,轉換抽象語法樹時,每遇到一個字符串,會根據其特征換算為一個hash值,插入到hashmap中。在之后如果遇到了hash值一致的字符串,會優先從里面取出來進行比對,一致的話就不會生成新字符串類。
- 2、緩存字符串時,根據字符串不同采取不同hash方式。
圖片
源碼
圖片
圖片
通俗易懂總結
字符串的數據存于 堆內存 中,棧內存 中只是存其 地址指針
當我們新建一個字符串時,V8會從內存中查找一下是否已經有存在的一樣的字符串,找到的話直接復用。如果找不到的話,則開辟一塊新的內存空間來存這個字符串,并把地址賦給變量。
大家有沒有想過,為什么字符串不能通過下標索引來進行修改呢?因為字符串的修改本質上只能是通過整個的修改,而不能局部修改。