1. 程式人生 > >jvm記憶體分配和垃圾回收機制

jvm記憶體分配和垃圾回收機制

問題:

1、垃圾回收目標物件? 2、什麼時間進行垃圾回收?(面試最常見的問題之一) 3、jvm怎樣進行垃圾回收?

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物件沒有在任何地方被引用,無法在任何地方通過反射訪問該方法

演算法圖解:

複製演算法處理前後

複製演算法

 標記-清除演算法處理前後

標記清除演算法

標記整理演算法

標記整理演算法