無論對于Java程式員還是大資料研發人員,JVM是必須掌握的技能之一,既是面試中經常問的問題,也是在實際業務中對程式進行調優、排查類似于記憶體溢位、堆疊溢位、記憶體泄漏等問題的關鍵,
在這里我為大家整理了各個知識點模塊整理檔案(微服務、資料庫、mysql、jvm、Redis等都有)和大廠面試真題,有需要的朋友可以點一點下方鏈接免費領取
鏈接:1103806531暗號:CSDN

本篇文章主要敘述JVM記憶體管理、直接記憶體、垃圾回收和常見的垃圾回收演算法:
運行時資料區域
JVM在執行一些基于JVM運行的程式,典型的如Java程式、Scala程式時,會把它所管理的記憶體劃分為多個不同的資料區域,這些區域有各個的作用、創建和銷毀時間,有的區域生命周期依賴于用戶執行緒的啟動和結束,有些區域則隨著虛擬機的啟動而存在,下圖展示了JVM在運行時的資料區域劃分:
1. 方法區
方法區是各個執行緒共享的記憶體區域,主要用于存放一些"自始至終都不會變化"的東西,比如final定義的常量、類的資訊(class實體)、靜態變數等、方法資訊,因為這些東西一旦被加載,是幾乎不會被GC的,所以方法區又被稱為永久代(注意一點,二者本質并不等價),
方法區有一部分叫常量池,用于存盤編譯期生成的一些字面變數、符號參考以及一些運行時產生的常量(如String常量池),方法區中的靜態區用于存放類變數、靜態塊等,
方法區又稱非堆,是有大小限制的,如果方法區使用記憶體超過了分配的大小,就會報類似OutOfMemory: PermGen Space的錯誤,
2. Java虛擬機堆疊
Java 虛擬機堆疊是執行緒私有的,它的生命周期與執行緒相同,為虛擬機執行Java方法即位元組碼服務,是描述Java方法執行時的記憶體模型,
每個方法執行時都會創建一個堆疊幀用于存盤區域變數表(比如編譯期可知的基本資料型別、物件參考等)、操作堆疊、動態鏈接、方法出口等資訊,每一個方法被呼叫至執行完成的程序,對應著一個堆疊幀在虛擬機堆疊中從入堆疊到出堆疊的程序,
如果執行緒請求的堆疊深度大于虛擬機所允許的深度,將會報StackOverFlowError;如果虛擬機堆疊無法申請到足夠的記憶體時會報OutOfMemoryError,
調整虛擬機堆疊大小的方式:-Xss,
3. 本地方法堆疊
本地方法堆疊為使用的到Native方法服務,本地方法介面都會使用某種本地方法堆疊,
當執行緒呼叫Java方法時,虛擬機會創建一個新的堆疊幀并壓入Java堆疊,然而,當它呼叫的是本地方法時,虛擬機會保持Java堆疊不變,不會在執行緒的Java堆疊中壓入新的堆疊幀,而是動態連接并直接呼叫指定的本地方法,
4. 堆
堆是JVM管理記憶體中最大的一塊區域,由Java執行緒共享,主要用來存盤new出來的物件和陣列,并且這塊區域隨著虛擬機的啟動而創建,堆可以處于邏輯上連續但物理上不連續的記憶體空間中,
堆是垃圾回收器管理的主要區域,可以細分為新生代和老年代,新生代又劃分為eden區,from survivor區、to survivor區,
物件在被創建時,首先在新生代進行分配,eden區存放新生成的物件,兩個survivor區用來存放新生代中每次垃圾回收后依然存活下來的物件,但是當創建新創建的物件非常大,該物件會直接進入老年代,
5. 程式計數器
程式計數器是執行緒私有的即每個執行緒都會有自己的程式計數器,用來記錄執行緒執行的位元組碼位置,是一個沒有OOM的區域,
直接記憶體
直接記憶體(direct memory)不屬于JVM運行時資料區的一部分,屬于堆外記憶體,會被頻繁使用,因此在設定各個記憶體范圍時要留出一部分物理記憶體,否則也容易拋出OutOfMemoryError,
垃圾收集
垃圾收集即GC,是JVM進行記憶體回收的處理程序,
開發人員更多的是關注業務需求的實作,而記憶體管理是交由JVM完成的,如果不進行或者錯誤的進行垃圾回識訓導致程式不穩定甚至崩潰,Java提供的GC功能可以自動監測物件是否超過作用域等從而達到自動回收記憶體的目的,可以有效防止記憶體泄露,有效的使用可用記憶體,
GC主要分為3種:minor GC、major GC和full GC,
minor GC是發生在新生代的,major GC是發生在老年代的,對于full GC出發的原因則比較多,比如老年代空間不足,它會出發stop world,處理不好往往會影響整個程式的穩定性嚴重會導致系統不可用,需要特別注意,
常見的垃圾回收演算法
1. 標記清除演算法
首先標記出所有需要回收的物件,在標記完成后統一回收所有被標記的物件,
存在如下兩個缺點:
- 效率低
需要先對要回收的物件進行標記,然后再統一清除,然而標記和清除兩個程序效率都很低下,
- 記憶體碎片問題
標記清除之后會產生大量不連續的記憶體碎片,空間碎片太多可能會導致以后在程式運行程序中需要分配較大物件時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作,影響性能,
2. 復制演算法
先將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊,當使用的這一塊的記憶體用完了,就將還存活著的物件復制到另外一塊上面,然后再把已使用過的記憶體空間一次清理掉,
優點:這樣使得每次都是對整個半區進行記憶體回收,記憶體分配時也就不用考慮記憶體碎片等復雜情況,只要移動堆頂指標,按順序分配記憶體即可,實作簡單,運行高效,
缺點:不適合物件存活率較高的場景,因為這種場景要進行較多的復制操作影響效率;實際可用記憶體變為分配記憶體的一半,因為每次只使用其中的一半記憶體,
3. 標記整理演算法
先標記(標記程序與標記清除演算法一樣),讓所有存活的物件都向一端移動,然后直接清理掉端邊界以外的記憶體,這樣可以解決記憶體碎片問題,
4. 分代收集演算法
就是針對Java堆記憶體中新生代、老年代等采用不同的垃圾回收演算法,如在新生代中,往往只有少量物件存活(最后會進入老年代),則適合用復制演算法,而老年代中物件存活率較高,沒有額外的空間對它進行分配擔保,就使用標記清除演算法,
當然實際應用中,使用什么演算法,要看使用的垃圾回收器,
最后
需要更多知識點和面試資料、面試真題的朋友可以點一點下方鏈接免費領取
鏈接:1103806531暗號:CSDN


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/195027.html
標籤:其他
上一篇:蘋果回應iPhone12用5G耗電快;央行:微信支付寶和數字人民幣不存在競爭關系;Win10X 將于年底簽署 RTM|極客頭條
