JS 的字符串是怎么分配內(nèi)存的?
可能大家都知道,字符串存在字符串常量池中,被?;蚨焉系淖兞恳?。如果變量的值是字符串字面量,則在棧上的變量直接引用字符串常量池中的字符串;如果是字符串是 new String 創(chuàng)建的,則會(huì)在堆上創(chuàng)建 String 對(duì)象,指向字符串常量池中的字符串,棧上變量指向堆中的 String 對(duì)象。
這個(gè)結(jié)論是對(duì)的么?
今天我們用 Chrome Devtools 的 Memory 工具證明下:
Memory 工具證明 String 的內(nèi)存分配方式
我們準(zhǔn)備這樣一段代碼:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- </head>
- <body>
- <script>
- const arr = [];
- setTimeout(() => {
- for(let i = 0;i< 10000;i++) {
- arr.push('guang');
- }
- }, 3000);
- const arr2 = [];
- setTimeout(() => {
- for(let i = 0;i< 10000;i++) {
- arr2.push(new String('guang'));
- }
- }, 5000);
- </script>
- </body>
- </html>
3s 的時(shí)候創(chuàng)建了一個(gè) 10000 個(gè)元素的數(shù)組 arr,數(shù)組元素是字符串常量 "guang"。
5s 的時(shí)候創(chuàng)建了一個(gè) 10000 個(gè)元素的數(shù)組 arr2,數(shù)組元素是 new String("guang")。
按照理論來(lái)說(shuō),arr 中的元素是直接引用字符串常量池的字符串,arr2 中的則是引用堆上的 String 對(duì)象,String 對(duì)象再引用字符串常量池的字符串。
我們用 Memory 工具來(lái)驗(yàn)證下。
Chrome Devtools 提供了 Memory 工具用于分析內(nèi)存中的對(duì)象:
一共有三種內(nèi)存分析工具:
- Snapshot:某個(gè)時(shí)間點(diǎn)的堆內(nèi)存快照
- TimeLine:實(shí)時(shí)的按照時(shí)間線顯示的內(nèi)存分配情況
- Sampling:采樣的方式收集內(nèi)存分配情況
我們想要看到按照時(shí)間線的實(shí)時(shí)分配情況,所以用第二種工具:TimeLine。
加載頁(yè)面,點(diǎn)擊錄制,右邊就會(huì)實(shí)時(shí)展示內(nèi)存分配情況:
我們錄到 6s 點(diǎn)擊停止。
可以看到有兩條豎線,分別代表了兩次內(nèi)存分配。
點(diǎn)擊第一次內(nèi)存分配,可以看到詳情:
可以看到,這個(gè)時(shí)間點(diǎn)創(chuàng)建了 string 和 array 兩種對(duì)象:
"guang" 這個(gè) string 的內(nèi)存地址是 @169541。
Array 的元素指向的也都是 @169541
這就驗(yàn)證了字符串常量池的存在,以及字符串字面量直接指向常量池中的字符串。
再來(lái)看下第二種內(nèi)存分配方式:
可以看到,創(chuàng)建了 String 的對(duì)象、array 變量(system 是 JS 引擎內(nèi)部分配的一些對(duì)象,不用關(guān)心):
String 對(duì)象引用了字符串常量池中的 @169541 的字符串 "guang"
而 Array 中的元素則是指向了不同的 String 對(duì)象的地址:
這再一次驗(yàn)證了字符串常量池的存在,以及 String 對(duì)象是在堆上分配內(nèi)存,然后指向字符串常量池的字符串。
證明完畢,確實(shí)如前面的結(jié)論所說(shuō):字符串存儲(chǔ)在字符串常量池中,字符串字面量直接指向常量池的字符串地址,String 對(duì)象會(huì)先在堆上分配空間,然后指向字符串常量池的字符串地址。
我們從始至終只創(chuàng)建了一次 "guang" 這個(gè)字符串,字符串常量池的好處顯而易見(jiàn)了:
而且,還可以得出一個(gè)結(jié)論,創(chuàng)建 String 對(duì)象的方式內(nèi)存開(kāi)銷大很多,建議用字符串字面量的方式:
從圖中可以直觀的對(duì)比出兩種方式的占用內(nèi)存的差別。
文中的測(cè)試代碼上傳到了 github: https://github.com/QuarkGluonPlasma/chrome-devtools-exercise
總結(jié)
Chrome Devtools 提供了 Memory 工具用于分析內(nèi)存,包括 Snapshot、TimeLine、Sample 三種工具,我們用其中的 TimeLine 工具實(shí)時(shí)分析了字符串的內(nèi)存分配,證明了字符串常量池的存在,以及字符串字面量、new String 兩種創(chuàng)建字符串方式的內(nèi)存上的差別。
建議盡量用字符串字面量,少用 new String 的方式創(chuàng)建字符串,在占據(jù)的內(nèi)存大小上還是有差距的。
證明過(guò)程中,我們也可以直觀的感受到字符串常量池的巨大好處。
原文鏈接:https://mp.weixin.qq.com/s/VucSMXz8tnNf_LyrXZLHsg