V8編譯流水線-函數調用

解釋執行和直接執行二進制代碼都使用了堆棧結構。那爲何使用棧結構管理函數調用?markdown

1. 爲何使用棧管理函數調用

一般函數有兩個主要的特性:異步

  • 函數能夠被調用。當函數調用發生時,執行代碼的控制權將從父函數轉移到子函數,子函數執行結束後,又會將代碼控制權返還給父函數。
  • 函數具備做用域機制。函數在執行時能夠將定義在函數內部的變量和外部環境隔離,函數內部定義的變量外部沒法訪問到,且函數執行結束後,內部變量也會被銷燬。

函數調用者(父函數)的生命週期老是長於被調用者(子函數),而且被調用者(子函數)的生命週期老是先於調用者(父函數)的生命週期結束。在函數資源分配和回收角度看,被調用函數(子函數)的資源分配老是晚於調用函數(父函數),且資源釋放也先於調用函數(父函數)。是一種後進先出LIFO的策略,棧結構也是這種模式,因此用棧來管理函數調用關係。函數

2. 棧如何管理函數調用

  • x=5,變量x第一次壓入到棧中。
  • y=6,變量y第一次壓入到棧中。
  • x=100,替換以前壓入棧的x的值,x的值由5改成100.
  • 計算x+y的值,賦值給z,並壓入到棧中。

函數在執行過程當中,其內部變量會按照執行順序被壓入到棧中。當父函數內嵌子函數時,子函數調用結束,就會把函數執行權交還給父函數,這個恢復的過程叫恢復現場。學習

恢復現場使用的方法就是在寄存器中保存一個永遠指向當前棧頂的指針,棧頂指針的做用就是告訴你往哪一個位置添加新元素,這個指針一般存放在esp寄存器中。同時增長另外一個ebp寄存器,用來保存當前函數(父函數)的起始位置,這個位置叫棧幀指針。大數據

每一個棧幀對應一個未運行完的函數,棧幀中保存了該函數的返回地址和局部變量。在JS中,函數執行過程也是相似的。調用一個新函數,v8會爲該函數建立棧幀,等函數執行結束以後,銷燬該棧幀。而棧結構的容量是固定的,若是不銷燬,很容易致使棧溢出。spa

3. 堆的做用

棧的缺點是在內存中不能分配一塊連續的較大的空間,因此棧空間是有限的。此時就有了堆空間,用來保存一些大數據。3d

堆空間中的數據不要求連續存放,從堆上分配內存沒有固定模式,能夠在任什麼時候候分配和釋放它。當遇到大數據時,會在堆中分配一塊空間,返回分配後的內存地址,該地址會被保存在棧中。好比下圖中的pp,棧中的地址指向了在堆中分配的空間地址。指針

當堆中的數據再也不須要的時候,須要對其進行銷燬。若是不及時銷燬,容易形成內存泄漏。code

4. 總結

  • 用棧結構管理函數的調用過程,稱爲調用棧。
  • 棧有最大容量限制,容易形成棧溢出。因此使用堆來存取大數據,而後在棧中保存堆的引用地址。
  • 解決棧溢出,也能夠將同步函數拆分紅異步函數處理。

寫在最後

V8相關的學習總結來自於極客時間李兵老師的課程《圖解goole V8》,若是想了解更多細節,能夠進課程查看。orm

相關文章
相關標籤/搜索