Java堆內存結構
java堆內存是垃圾回收器管理的主要區域,也被稱為GC堆。
為了方便垃圾回收,堆內存被分為新生代、老年代和永久代。
新創建的對象的內存會在新生代中分配,達到一定存活時長后會移入老年代,而永久代存儲的是類的元數據,在JDK1.8之后永久代被元空間取代,元空間不再位于堆內存中,而是位于本地內存,它們都是方法區的實現。
如何判斷一個對象是垃圾呢?
堆中幾乎存放了所有的對象實例,對堆進行垃圾回收首先是去判斷哪些對象是垃圾。
引用計數法
在對象的對象頭中會維護一個引用計數器,每有一個地方引用它,計數器就會加1,當斷開引用了就會減1,當計數器的值為0時,說明這個對象是個垃圾。
這種方法簡單,效率高,但是會有循環引用的問題。如果一個a對象和b對象之間互相引用,除此之外沒有任何引用,那么ab對象的引用計數器的值都為1,就得不到釋放。
可達性分析算法
以GC Roots對象為起點,向下搜索,節點走過的路勁成為引用鏈,如果一個對象沒有引用鏈可以到達GC Roots對象,那么這個對象就是垃圾。
哪些對象是GC Roots對象?
- 虛擬機棧中(棧幀中局部變量表)引用的對象
- 本地方法棧中引用的對象
- 方法區中的靜態變量和常量引用的對象
- 同步鎖持有的對象
引用的類型
判斷對象是否是垃圾還與引用的類型有關。
強引用:當我們去new對象的時候,創建的引用就是強引用,如果一個對象具有強引用,垃圾回收器寧愿拋出OOM錯誤也不會回收這個對象。
軟飲用:如果內存充足就不回收,內存不足就會被回收。
弱引用:不管內存充不充足都會被回收。
虛引用:"虛引用"顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。
垃圾回收算法
標記-清除算法
通過可達性分析算法找到存活對象,也就是在GC Roots引用鏈上的對象,標記它們,然后清除未標記的對象。
這種算法的效率較高,而且垃圾回收后有太多內存碎片,如果是數組這種需要連續內存的對象,可能找不到內存空間。
標記-整理算法
找到存活的對象,標記它們,并讓它們向同一端移動,然后清除端邊界之外的內存。
這種算法解決了內存碎片化的問題,但由于要整理,所以效率不高,適合老年代這種垃圾回收頻率不高的場景。
標記-復制算法
標記-復制算法將內存分為相同的兩塊,只使用其中一塊內存,當那一塊內存使用完之后,找到存活的對象,標記它們,然后把它們移到另一塊內存上,再把之前使用的那塊內存全部清除掉。
這種算法效率較高,而且內存沒有碎片化,但是內存使用率太低,只有一半。
分代收集算法
通過對象存活時間的不同將內存分為了新生代和老年代。
其中新生代又分為伊甸園區(eden),幸存者區(S0、S1)。
新創建的對象會被分配到eden區,當eden區內存不足時,會采用標記-復制算法將eden區和
s0區(第一次復制沒有對象)的存活對象復制到s1區,然后清除eden區和s0區的內存。之后又創建對象,當eden又滿了之后就把eden區和s1區的存活對象復制到s0區,清除eden區和s1區的內存。由此循環,如果存活對象多次都沒有被回收,會移到老年代中。
為什么要分代收集?為什么堆要被分為新生代和老年代?
因為這樣可以根據各個年代的特點選擇不同的垃圾回收算法,新生代的對象存活率低,就可以選擇標記-復制算法,復制少量存活對象就可以完成垃圾回收;而老年代對象存活率高,不適合復制算法,采用標記-清除或者標記-整理算法。
什么是Minor GC、Full GC?
當進行垃圾回收的時候,會暫停所有線程去完成垃圾回收。
Minor GC :發生在新生代的垃圾回收,當eden區滿了會觸發,暫停時間較短。
Full GC:對新生代和老年代進行完整的垃圾回收,當老年代內存不足時會觸發,暫停時間長,要盡量避免。
垃圾回收器
Serial垃圾回收器
serial垃圾回收器也叫串行垃圾回收器,是一個單線程的垃圾回收期,進行垃圾回收的時候,會暫停其他所有的線程,直到收集結束。
Parallel垃圾回收器
也叫并行垃圾回收器,使用多線程進行垃圾回收,垃圾回收時也會暫停其余所有的線程。
JDK1.8默認使用此垃圾回收器。
CMS垃圾回收器
也叫并發垃圾回收器,它的暫停時間很短,GC線程和用戶線程并發工作。
CMS垃圾回收器的回收過程分為四個階段:
初始標記:去標記和GC Roots對象直接連接的對象,會暫停其余線程,但時間很短。
并發標記:之后根據引用鏈標記其余的可達對象,不會暫停其余線程。
重新標記:防止在并發標記期間有新對象創建而漏標,會暫停其余線程,暫停時間短。
并發清除:清除未標記的對象,不會暫停其余線程。
G1垃圾回收器
G1垃圾回收器是JDK9之后默認的垃圾回收器。它將內存分為多個區域,每個區域都可以充當eden區,Survivor區,old區,humongous區,其中humongous區專門用于存放大對象。
G1垃圾回收器采用復制算法回收垃圾,主要有兩種回收方式:Young?GC和Mixed GC
G1垃圾回收流程
Young?GC(對年輕代的回收):
首先新創建的對象會被分配到eden區,當eden區內存達到閾值后,觸發Young GC,會選一個空區域作為幸存者區,然后將存活對象復制到幸存者區,并釋放eden區的內存。
? ? ? ? ? ? ? ? ?
當eden再次達到閾值,觸發Young GC,選出一個新的空區域作為幸存者區,將eden區和舊幸存者區的存活對象復制到新幸存者區,并釋放eden和舊幸存者區的內存,當經過多次Yong GC后還存活的對象會被復制到老年代。
Mixed GC(對年輕代和部分老年代的回收):
當老年代的內存達到一個閾值之后,會去并發標記老年代中的存活對象,無需暫停用戶線程。
然后會去重新標記,防止并發標記導致漏標,此時會暫停用戶線程。
之后進行Mixed GC,對年輕代和部分老年代進行垃圾回收。對于年輕代,將eden區和舊幸存者區的存活對象復制到新幸存者區,并釋放內存;對于老年代,不會去回收所有的老年代,而是選出存活對象較少的老年代,將它們的存活對象復制到一個新的老年代,然后釋放內存。