JAVA垃圾回收機制


JAVA 內存運行時區域中的程序計數器、虛擬機棧、本地方法棧隨線程而生滅,棧中的棧幀隨着方法的進入和退出而有條不紊地執行着出棧和入棧操作。每一個棧幀中分配多少內存基本上是在類結構確定下來時就已知的(盡管在運行期會由 JIT 編譯器進行一些優化),因此這幾個區域的內存分配和回收都具備確定性,不需要過多考慮回收的問題,因為方法結束或者線程結束時,內存自然就跟隨着回收了。

而 Java 堆不一樣,一個接口中的多個實現類需要的內存可能不一樣,一個方法中的多個分支需要的內存也可能不一樣,我們只有在程序處於運行期間時才能知道會創建哪些對象,這部分內存的分配和回收都是動態的,垃圾收集器所關注的是這部分內存。

自動垃圾收集是查看堆內存,識別正在使用哪些對象以及哪些對象未被刪除以及刪除未使用對象的過程。

使用中的對象或引用的對象意味着程序的某些部分仍然維護指向該對象的指針。程序的任何部分都不再引用未使用的對象或未引用的對象,因此可以回收引用對象使用的內存。

在C語言中,內存的分配和釋放是手動的過程,在Java語言中內存的分配和回收是由垃圾收集器自動處理的。

該過程的第一步稱為標記。這是垃圾收集器識別哪些內存正在使用而哪些不在使用的地方。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-47hfuEpA-1598418362952)(/Users/lipan/app/typora-pic/image-20200826124122668.png)]

引用計數法

引用計數法主要是通過給對象中添加一個引用計數器,每當一個地方引用它時,計數器的值就加1,當引用失效時,計數器的值就減1,當計數器的值為0時,對象就是不可能被使用的,可以對其進行垃圾回收。引用計數法的實現簡單,效率也比較高,但是它很難解決對象之間循環引用的問題。

可達性分析算法

可達性分析算法,也稱為根搜索算法,這個算法的基本思路就是通過一系列的名為“GC Roots”的對象為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到“GC Roots”沒有任何引用鏈相連,則證明對象是不可用的,可以對其進行垃圾回收。

簡單來說,將對象及其引用關系看作一個圖,選定活動的對象作為“GC Roots”,然后跟蹤引用鏈條,如果一個對象和”GC Roots“之間不可達,也就是不存在引用,即可認為是可回收對象。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-egP3UXyd-1598418395248)(/Users/lipan/app/typora-pic/image-20200826124147149.png)]

引用類型:

  • 強引用(Strong Reference):最常見的普通引用,只要還有強引用指向一個對象,就不會回收
  • 軟引用(Soft Reference):JVM認為內存不足阿時候,才會去試圖回收軟引用指向的對象(緩存場景)
  • 弱引用(Weak Reference):雖然是引用,但隨時有可能被回收掉
  • 虛引用(Phantom Reference):不能通過它訪問對象,提供對象被finalize以后,執行指定邏輯的機制。

可達性級別:

  • 強可達(Strongly Reachable):一個對象可以有一個或多個線程可以不通過各種引用訪問到的情況
  • 軟可達(Softly Reachable):只能通過軟引用才能訪問到的狀態
  • 弱引用(Weakly Reachable):只能通過弱引用訪問時的狀態,當弱引用被清除的時候,就符合銷毀條件
  • 幻像可達(Phantom Reachable):不存在其他引用,並且finalize過了,只有幻像引用指向這個對象
  • 不可達(Unreachable):意味着對象可以被清除了

目前主流廣泛使用的垃圾收集算法,主要有標記-清除算法(Mark-Sweep),復制算法(Coping)以及標記-整理算法(Mark-Compact)

標記-清除(Mark-Sweep)算法:首先標識出所有要回收的對象,然后進行清除。

標記、清除過程效率有限,有內存碎片化問題,不適合特別大的堆;其他收集算法基本都是基於標記-清除的思路進行改進。[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-aCmvcCVw-1598418362955)(/Users/lipan/app/typora-pic/image-20200826124220648.png)]

復制(Copying)算法:划分兩塊同等大小的區域,收集時將活着的對象復制到另一塊區域。

拷貝過程中將對象順序放置,就可以避免內存碎片化。復制+預留內存,有一定的浪費。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MdHk2Kjt-1598418362960)(/Users/lipan/app/typora-pic/image-20200826124243032.png)]

標記-整理(Mark-Compact):類似於標記-清除,但為了避免內存碎片化,它會在清理過程中將對象移動,以確保移動后的對象占用連續的內存空間

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BN74DljJ-1598418362961)(/Users/lipan/app/typora-pic/image-20200826124304112.png)]

根據對象的存活周期,將內存划分為幾個區域,不同區域采用合適的垃圾收集算法。目前主流的JVM一般將堆內存分為新生代和老年代(大小比列為1:2),而新生代又被分為了Eden、From Survivor、To Survivor(大小比列為8:1:1)。在新生代中,每次垃圾收集時都發現有大量對象死去,只有少量存活,那么就可以選用復制算法,只需付出少量對象的復制成本就可以完成收集。在老年代中,對象存活率高,就可以使用標記-清除,標記-整理算法。

新生代幾乎是所有JAVA對象出生的地方,JAVA對象申請的內存和存放都是在這個地方,JVM每次只會使用新生代中的Eden和其中一塊Survivor來為對象服務,所以無論什么時候,都會有一塊survivor空間空閑。當對象經過一次minor gc后仍然存活,並且能夠被另外一塊survivor所容納,則使用復制算法將這些仍然存活的對象復制到另外一塊survior區域中,然后清理掉eden和之前使用的survivor區域,並將這些存活的對象年齡+1,以后對象在survivor中每熬過一次minor gc則年齡增加1,當年齡達到某個值時(默認15,通過設置參數-XX:MaxTenuringThreshold來設置),這些對象就會進入老年代。

當然,對於一些較大的對象可以直接進入老年代,可以根據-XX:+PretenureSizeThreshold設置大對象進入老年代的閾值。

新生代采用復制算法:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KsyAKvIi-1598418362962)(/Users/lipan/app/typora-pic/image-20200826124323991.png)]

老年代使用標記-整理算法:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gVcX1OW4-1598418362963)(/Users/lipan/app/typora-pic/image-20200826124341554.png)]

垃圾收集算法只是內存回收的理論方法,垃圾收集器才是內存回收的具體實現。Java虛擬機規范中對垃圾收集器的實現沒有具體的規定,不同廠商,不同版本的垃圾收集器可能會差別很大。目前常見的垃圾收集器主要有Serial收集器,ParNew收集器,Parallel Scavenge收集器,Serial Old收集器,Parallel Old收集器,CMS收集器,G1收集器。這些垃圾收集器各有優劣,一般都是組合在一起使用,下圖展示了在新生代和老年代可用的垃圾收集器組合:

Serial收集器

Serial收集器是一個串行收集器,使用單個線程來執行所有垃圾收集工作,適合單處理器機器,GC在工作的時候將暫停其他所有的工作線程,即”Stop The World“。Serial收集器是一個新生代的單線程收集器,它使用復制算法,是虛擬機在client模式下的默認新生代收集器,可以通過設置參數-XX:+UseSerialGC來使用。

Serial Old收集器

Serial Old收集器是Serial收集器的在老年代使用的版本,它采用了標記-整理(Mark-Compact)算法,可以通過設置參數-XX:+UseSerialOldGC來使用。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-UqMDpnkP-1598418362964)(/Users/lipan/app/typora-pic/image-20200826124416201.png)]

ParNew收集器

ParNew收集器可以理解為串行收集器的多線程版本,其整體算法和Serial比較相似,除了使用多線程進行垃圾回收,其他的基本和Serial收集器一樣。ParNew收集器是虛擬機在server模式下的默認新生代收集器,可以通過設置參數-XX:+UseParNewGC來使用。

默認情況下它開啟的線程數和CPU數量相同,也可以通過設置參數-XX:ParallelGCThreads來配置垃圾收集的線程數。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-mOxJQRB3-1598418362964)(/Users/lipan/app/typora-pic/image-20200826124628666.png)]

Parallel Scavenge收集器

Parallel Scavenge收集器也是一個新生代的並行垃圾收集器,使用的也是復制算法。Parallel Scavenge收集器的目標是達到一個可控制的吞吐量。吞吐量就是CPU運行用戶代碼的時間與CPU消耗的總時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)。

Parallel Scavenge收集器提供-XX:MaxGCPauseMillis來控制最大垃圾收集停頓時間和-XX:GCTimeRatio來直接設置吞吐量的大小。

Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和標記-整理算法,可以通過設置參數-XX:+UseParallelOldGC來使用。

CMS收集器

CMS收集器是一個並發收集器,CMS即Concurrent Mark Swap,主要用於老年代,使用標記-清除算法,可以通過設置參數-XX:+UseConcMarkSweepGC來使用。CMS收集器可以並行的執行用戶程序和垃圾回收,這樣就可以減少回收的停頓時間。CMS收集器設計的主要目的就是為了減少回收停頓時間,目前很多的Java應用都集中在互聯網網站或B/S系統服務端,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,CMS收集器非常符合這類應用的要求。當然,CMS收集器也有自己的缺點,它會占用更多的CPU資源,並和用戶線程爭搶,同時由於采用標記-清除算法,存在着內存碎片化的問題,長時間運行等情況下可能發full gc導致惡劣的停頓。

CMS收集器垃圾收集的整個過程有4個步驟,初始標記,並發標記,重寫標記以及並發清除。其中初始標記和重寫標記任然需要”Stop The World”,但是速度很快,整個過程中耗時的操作在並發標記和並發清除階段,在這個過程中,收集器線程都可以和用戶線程一起工作。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KSivzCgS-1598418362965)(/Users/lipan/app/typora-pic/image-20200826124756418.png)]

G1收集器

G1(Garage First)收集器是當前垃圾收集器技術最前沿的成果,在JDK7中開始使用(可以通過配置-XX:+UseG1GC使用),從JDK9開始G1已經成為JVM默認的垃圾收集器。G1垃圾收集器也是以關注延遲為目標、服務器端應用的垃圾收集器,其目標就是取代CMS垃圾收集器。

G1采用了分區(Region)的思路,將整個堆空間分成若干個大小相等的內存區域,每次分配對象空間將逐段地使用內存,這樣最大的好處就是化整為零,避免全內存掃描,只需要按照區域來進行掃描即可。啟動時可以通過參數-XX:G1HeapRegionSize=n可指定分區大小(1MB~32MB,且必須是2的冪),默認將整堆划分為2048個分區。在堆的使用上,整個內存分區不存在物理上的年輕代和老年代的區別,也不需要完全獨立的 Survivor to space 堆做復制准備。G1 只有邏輯上的分代概念,或者說每個分區都可能隨 G1 的運行在不同代之間前后切換。

G1收集器整體采用標記-整理算法,局部是通過是通過復制算法,不會產生內存碎片,能充分利用多 CPU、多核環境硬件優勢,盡量縮短”Stop The World”的時間

下圖左側為G1收集器的內存划分概圖,紅色為新生代(沒有S的為eden區域,有S的為survivor區域),藍色為老年代(帶有H的表示humongous ,存放humongous objects,即大於等於region一半的對象)。右側為G1收集器的垃圾收集環圖,G1的垃圾收集在層次上可以分為兩個階段,young-only階段和Space-reclamation階段,在young-only階段又分為Initial Mark ,Remark,Cleanup。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-U7wNhG1d-1598418362966)(http://lions.qicp.vip/wordpress/wp-content/uploads/2020/05/%E5%9B%BE%E7%89%87-8.png)]

在這里插入圖片描述

https://www.cnblogs.com/coding-diary/p/11722306.html

https://blog.csdn.net/tiankongyun123/article/details/106631599


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM