jvm記憶體分配和垃圾回收機制
問題:
1、垃圾回收目標物件? 2、什麼時間進行垃圾回收?(面試最常見的問題之一) 3、jvm怎樣進行垃圾回收?
jvm記憶體分配
執行緒共享區域
1、 堆
2、方法區
執行緒私有區域
1、jvm棧
2、本地方法棧
3、程式計數器
由於虛擬機器棧,本地方法棧、程式計數器是執行緒私有的,隨執行緒而滅,因此這幾個區域的記憶體分配和回收都具備確定性,就不需要多考慮回收問題。但是堆和方法區則不一樣,這部分記憶體的分配和回收是動態的,正是垃圾收集器所需要關注的部分。
堆
java堆是jvm記憶體管理中最大的一塊,執行緒共享。在jvm啟動的時候建立。此區域唯一目的就是存放物件例項,幾乎所有的物件例項都在這裡分配記憶體。堆又細分為新生代和老年代
1、新生代
新生代又分為Eden空間、From Survivor空間、To Survivor空間
垃圾回收的型別:minor GC
什麼時間進行垃圾回收:當jvm無法為一個新的物件分配空間時會觸發Minor GC,比如當Eden區滿了。
新生代中垃圾回收演算法為複製演算法,複製演算法的基本思想是將記憶體分為兩塊,每次只用其中一塊,當這一塊記憶體使用完,就將還活著的物件複製到另一塊上面。複製演算法不會產生記憶體碎片,效率比較高。
在GC開始的時候,物件只會存在於eden區,和名為“From”的Survior區,Survior區“to”是空的。緊接著GC
eden區中所有存活的物件都會被複制到“To”,而在from區中,仍存活的物件會根據他們的年齡值來決定去向,
年齡到達一定只的物件會被複制到老年代,沒有到達的物件會被複制到to survior中,經過這次gc後,eden區和from
survior區已經被清空。這個時候,from和to會交換他們的角色,也就是新的to就是上次GC前的from。
特點:因為大部分物件都是從Eden區開始的,同時Eden區不會分配的很大,所以GC會頻繁進行,效率很高。
2、老年代
垃圾回收型別:Full GC 【對整個堆進行整理】
什麼時間進行垃圾回收:老年代被寫滿時,或者呼叫System.gc()
老年代中採用的GC演算法為標記-清除演算法或者標記-整理演算法。
標記-清除演算法為:首先標記出要進行GC【垃圾回收】的物件,標記完再進行GC。這種演算法效率不高,並且會產生很多記憶體碎片。
標記-整理演算法:先標記,但是不同的是標記完成後不是立刻執行GC,而是先將不需要GC的物件移動到一端,然後在邊界外再對要回收的物件進行GC,成本高、但是不會產生碎片。
特點:因為需要對整個堆進行回收,所以執行時間比較長,次數比較少。
3、擴充套件
關於物件的分配:物件優先在Eden區域分配,大物件、長期存活的物件會進行老年代。這裡的長期存活是根據新生代中的物件年齡閾[yu]值來定義的,物件剛分配到新生代年齡為1,每進行一次GC物件的年齡會加1,hotspot中預設的閾值是15,也就是說物件年齡達到15會被分到老年區。
回收的主要目標物件:主要根據可達性分析演算法,對於可達性分析演算法,它是通過一系列稱為"GC Roots"的物件作為起始點,當一個物件與GC Roots沒有任何引用鏈相接的時候,那麼就是可以回收的。如果有引用鏈相連線,那麼這個物件不可用回收。
方法區
方法區執行緒共享,用來儲存已被JVM載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。
這個區域很少進行垃圾回收,回收目標主要是針對常量池的回收和對型別的解除安裝。
無用類需要滿足3個條件:
該類所有例項都已經被回收,即java堆中不存在該類的任何例項;
載入該類的ClassLoader已經被回收;
該類對應的java.lang.Class物件沒有在任何地方被引用,無法在任何地方通過反射訪問該方法
演算法圖解:
複製演算法處理前後
標記-清除演算法處理前後
標記整理演算法