1. 程式人生 > 實用技巧 >呼叫堆疊(三)-記憶體空間

呼叫堆疊(三)-記憶體空間

三種資料結構

1、棧資料結構
棧的結構就是後進先出(LIFO),如下圖:

2、堆資料結構
堆資料結構是一種樹狀結構,它的存取資料的方式與書架和書非常相似。我們只需要知道書的名字就可以直接取出書了,並不需要把上面的書取出來。JSON格式的資料中,我們儲存的key-value可以是無序的,因為順序的不同並不影響我們的使用,我們只需要關心書的名字。

3、佇列
佇列是一種先進先出(FIFO)的資料結構,這是事件迴圈(Event Loop)的基礎結構。

變數的存放

1、基本型別,儲存在棧記憶體中,因為這些型別在記憶體中分別佔有固定大小的空間,通過按值來訪問。

2、引用型別,儲存在堆記憶體中,因為這種值大大小不固定,因此不能把它們儲存在棧記憶體中,但記憶體地址大小是固定的,因此儲存在堆記憶體中,在棧記憶體中存放的只是該物件的訪問地址。當查詢引用型別的變數時,先從棧中讀取記憶體地址,然後再通過地址找到堆中的值。對於這種,我們把它叫做按引用訪問。

在計算機的資料結構中,棧比堆的運算速度快,Object是一個複雜的結構且可以擴充套件:陣列可擴充,物件可以新增屬性,都可以增刪改查。將它們放在堆中是為了不影響棧的效率。而是通過引用的方式查詢到堆中的實際物件再進行操作。所以查詢引用型別值得時候先去棧查詢再去堆查詢。

看幾個例子:
eg1:

  var a = 20;
  var b = a;
  b = 30;

  // 這時a的值是多少?

eg2:

 var a = { name: '前端開發' }
 var b = a;
 b.name = '進階';

 // 這時a.name的值是多少 

eg3:

  var a = { name: '前端開發' }
  var b = a;
  a = null;

  // 這時b的值是多少

現在來解答一下,三個問題的答案分別是:20、'進階'、{name: '前端開發'}

  • 對於問題1,a, b都是基本型別,它們的值是儲存在棧中的,a, b分別有各自獨立的棧空間,所以修改了b的值以後,a的值並不會發生變化。
  • 對於問題2,a、b都是引用型別,棧記憶體中存放地址指向堆記憶體中的物件,引用型別的複製會為新的變數自動分配一個新的值儲存在變數物件中,但只是引用型別的一個地址指標而已,實際指向的是同一個物件,所以修改b.name的值後,相應的a.name也就發生了改變。
  • 對於問題3,首先要說明的是null是基本型別,a = null之後只是把a儲存在棧記憶體中地址改變成了基本型別null,並不會影響堆記憶體中的物件,所以b的值不受影響。

記憶體空間管理

JavaScript的記憶體生命週期是:
1、分配你所需的記憶體
2、使用分配到的記憶體(讀、寫)
3、不需要時將其釋放、歸還

JavaScript有自動垃圾收集機制,最常用的是通過標記清除的演算法來找到哪些物件是不再繼續使用的,使用a = null其實僅僅只是做了一個釋放引用的操作,讓a原本對應的值失去引用,脫離執行環境,這個值會在下一次垃圾收集器執行操作時被找到並釋放。

在區域性作用域中,當函式執行完畢,區域性變數也就沒有存在的必要了,因此垃圾收集器很容易做出判斷並回收。但是全域性變數什麼時候需要自動釋放記憶體空間則很難判斷,因此在開發中,需要儘量避免使用全域性變數。