通過 Chrome Devtools 的 Memory 工具證明 string 的內(nèi)存分配方式
JS 的字符串是怎么分配內(nèi)存的?
可能大家都知道,字符串存在字符串常量池中,被?;蚨焉系淖兞恳谩H绻兞康闹凳亲址置媪?,則在棧上的變量直接引用字符串常量池中的字符串;如果是字符串是 new String 創(chuàng)建的,則會在堆上創(chuàng)建 String 對象,指向字符串常量池中的字符串,棧上變量指向堆中的 String 對象。
這個結(jié)論是對的么?
今天我們用 Chrome Devtools 的 Memory 工具證明下:
Memory 工具證明 String 的內(nèi)存分配方式
我們準備這樣一段代碼:
- <!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 的時候創(chuàng)建了一個 10000 個元素的數(shù)組 arr,數(shù)組元素是字符串常量 "guang"。
5s 的時候創(chuàng)建了一個 10000 個元素的數(shù)組 arr2,數(shù)組元素是 new String("guang")。
按照理論來說,arr 中的元素是直接引用字符串常量池的字符串,arr2 中的則是引用堆上的 String 對象,String 對象再引用字符串常量池的字符串。
我們用 Memory 工具來驗證下。
Chrome Devtools 提供了 Memory 工具用于分析內(nèi)存中的對象:
一共有三種內(nèi)存分析工具:
- Snapshot:某個時間點的堆內(nèi)存快照
- TimeLine:實時的按照時間線顯示的內(nèi)存分配情況
- Sampling:采樣的方式收集內(nèi)存分配情況
我們想要看到按照時間線的實時分配情況,所以用第二種工具:TimeLine。
加載頁面,點擊錄制,右邊就會實時展示內(nèi)存分配情況:
我們錄到 6s 點擊停止。
可以看到有兩條豎線,分別代表了兩次內(nèi)存分配。
點擊第一次內(nèi)存分配,可以看到詳情:
可以看到,這個時間點創(chuàng)建了 string 和 array 兩種對象:
"guang" 這個 string 的內(nèi)存地址是 @169541。
Array 的元素指向的也都是 @169541
這就驗證了字符串常量池的存在,以及字符串字面量直接指向常量池中的字符串。
再來看下第二種內(nèi)存分配方式:
可以看到,創(chuàng)建了 String 的對象、array 變量(system 是 JS 引擎內(nèi)部分配的一些對象,不用關心):
String 對象引用了字符串常量池中的 @169541 的字符串 "guang"
而 Array 中的元素則是指向了不同的 String 對象的地址:
這再一次驗證了字符串常量池的存在,以及 String 對象是在堆上分配內(nèi)存,然后指向字符串常量池的字符串。
證明完畢,確實如前面的結(jié)論所說:字符串存儲在字符串常量池中,字符串字面量直接指向常量池的字符串地址,String 對象會先在堆上分配空間,然后指向字符串常量池的字符串地址。
我們從始至終只創(chuàng)建了一次 "guang" 這個字符串,字符串常量池的好處顯而易見了:
而且,還可以得出一個結(jié)論,創(chuàng)建 String 對象的方式內(nèi)存開銷大很多,建議用字符串字面量的方式:
從圖中可以直觀的對比出兩種方式的占用內(nèi)存的差別。
文中的測試代碼上傳到了 github: https://github.com/QuarkGluonPlasma/chrome-devtools-exercise
總結(jié)
Chrome Devtools 提供了 Memory 工具用于分析內(nèi)存,包括 Snapshot、TimeLine、Sample 三種工具,我們用其中的 TimeLine 工具實時分析了字符串的內(nèi)存分配,證明了字符串常量池的存在,以及字符串字面量、new String 兩種創(chuàng)建字符串方式的內(nèi)存上的差別。
建議盡量用字符串字面量,少用 new String 的方式創(chuàng)建字符串,在占據(jù)的內(nèi)存大小上還是有差距的。
證明過程中,我們也可以直觀的感受到字符串常量池的巨大好處。