在執行Spark的應用程序時,Spark cluster將啟動兩個JVM進程:驅動程序和執行器:
Spark管理的內存主要分為四個區域:
作為壹個JVM進程,執行程序的內存管理是基於JVM的內存管理的,Spark更詳細地分配JVM的堆上空間以充分利用內存。同時,Spark引入了堆外內存,可以直接在工作節點的系統內存中創建空間,進壹步優化了內存的使用。
堆中內存的大小由執行器配置——內存或Spark。啟動Spark應用程序時的executor.memory參數。在Executor * * *中運行的並發任務享有JVM堆中的內存,這些任務在緩存RDD數據和廣播數據時占用的內存被計劃為存儲內存,而這些任務在執行Shuffle時占用的內存被計劃為執行內存,其余的沒有特別計劃,Spark內部或用戶定義的Spark應用程序中的對象實例被占用。在不同的管理模式下,這三部分所占的空間是不同的。
Spark對堆中內存的管理是壹種邏輯上的“規劃”管理,因為對象實例占用的內存的申請和釋放都是由JVM完成的,Spark只能在申請後和釋放前記錄這些內存。我們來看看它的具體流程:
為了進壹步優化內存的使用並提高Shuffle時的排序效率,Spark引入了堆外內存,可以直接在工作節點的系統內存中創建空間來存儲序列化的二進制數據。使用JDK不安全API(從Spark 2.0開始),堆外存儲內存不再基於Tachyon,而是基於JDK不安全API,就像堆外執行內存壹樣。Spark可以直接操作系統堆外內存,從而減少不必要的內存開銷、頻繁的GC掃描和回收,並提高處理性能。外部存儲器可以精確地申請和釋放,序列化數據占用的空間可以精確地計算,因此與內部存儲器相比,它減少了管理的難度和錯誤。
默認情況下,堆外內存是不啟用的,這可以通過配置spark.memory.offHeap.enabled參數來啟用,堆外空間的大小由spark.memory.offHeap.size參數設置。除了沒有其他空間之外,堆外內存的劃分方式與堆內內存相同,所有運行中的並發任務都享有存儲內存和執行內存。
Spark 1.6之後默認為統壹管理模式,而Spark 1.6之前的靜態管理模式仍然保留。您可以通過配置spark . memory . use legacy mode = true參數來啟用StaticMemoryManager模式。讓我們介紹兩種內存管理模型的演變。
在Spark最初采用的靜態內存管理機制下,存儲內存、執行內存和其他內存的大小在Spark應用程序運行期間是固定的,但用戶可以在應用程序啟動前對其進行配置。堆中的內存分配如下:
Spark 1.6之後引入的統壹內存管理機制與靜態內存管理不同,存儲內存和執行內存* * *共享相同的空間,可以動態占用彼此的空閑區域。如下圖所示:
其中最重要的優化在於動態占用機制,其規則如下:
新版本引入了新的配置項目:
借助統壹的內存管理機制,Spark在壹定程度上提高了堆內和堆外內存資源的利用率,降低了開發人員維護Spark內存的難度,但並不意味著開發人員可以高枕無憂。例如,如果存儲內存空間太大或緩存的數據太多,將導致頻繁的總垃圾收集並降低任務執行的性能,因為緩存的RDD數據通常會在內存中停留很長時間。因此,為了充分發揮Spark的性能,開發人員需要進壹步了解存儲內存和執行內存各自的管理方法和實現原理。