JVM的重要性不言而喻了,若是把java的應用程序比做一輛跑車,那麼JVM就是這輛車的發動機,沒有它,java程序就成了空中樓閣,無根浮萍。而在JVM中有一塊內存區域叫作運行時數據區域,存儲了運行時所須要的全部對象,而Heap Area則是其中最大的一塊。java
內存畢竟不是無限的,因此就須要一種機制來將再也不使用的對象進行回收,這種機制就是今天咱們要講的GC。程序員
更多精彩內容且看:算法
小師妹:F師兄,你相信這個世界有輪迴嗎?spring
師兄我是一個堅決的無神論者,活在當下就行了,何須操心後面的輪迴呢?併發
小師妹:F師兄,這個你就不懂了,意識是組成腦的原子羣的一種組合模式,咱們大腦的物質基礎和一塊石頭沒有什麼不一樣。當咱們掌握大腦的組合方式,而後重構,咱們的意識就重現了,這就是輪迴。這但是量子理論中提到的觀念哦。jvm
哇,小師妹何時這麼厲害了,都開始探討這麼高深的話題了。F師兄我實在是跟不上節奏啊。spring-boot
小師妹,F師兄,我是怕你尷尬,想引出java對象的生命週期這個話題嘛。性能
量子理論我不熟,java對象我還沒怕過誰。區塊鏈
對象的生命週期其實很簡單:建立,使用中,最後被銷燬。spa
舉個最簡單的建立對象的例子:
Object obj = new Object();
對象建立的時候,將會爲該對象分配特定的空間。
對象建立以後,就能夠被其餘的對象使用,若是其餘的對象有使用該對象,那麼咱們成爲該對象被引用了。
當一個對象沒有被其餘對象引用的時候,咱們就稱爲該對象能夠被回收了。在Java中,對象的回收是由GC來負責的。
小師妹:F師兄,我以爲垃圾回收好像挺簡單的,咱們爲每一個對象維持一個指針計數器,每引用一次就加一,這樣不就能夠實現垃圾回收器了嗎?
底層原理是這麼一個道理,可是JVM須要一種更加高效的算法來保證垃圾回收的效率,同時也不會影響正在運行的程序。
接下來咱們將會介紹一下,在JVM中比較經常使用幾個垃圾回收算法:
Mark and sweep是最最簡單的垃圾回收算法,簡單點講,它能夠分爲兩個步驟:
標記live對象聽起來很簡單,就是掃描堆中的對象,看這些對象是否被引入。
可是這裏有一個問題,若是是兩個對象互相引用的時候,而這兩個對象實際上並無被外部的對象所引用,那麼這兩個對象實際上是應該被回收的。因此咱們還須要解決一個關鍵性的問題:從哪裏開始掃描的問題。
JVM定義了一些Root對象,從這些對象開始,找出他們引用的對象,組成一個對象圖。全部在這個圖裏面的對象都是有效的對象,反之不在對象圖中的對象就應該被回收。有效的對象將會被Mark爲alive。
這些Root對象包括:正在執行的方法中的本地對象和輸入參數。活動的線程,加載類中的static字段和JNI引用。
注意,這種遍歷實際上是有個缺點的,由於爲了找到對象圖中哪些對象是live的,必須暫停整個應用程序,讓對象變成靜止狀態,這樣才能構建有效的對象圖。後面咱們會介紹更加有效的垃圾回收算法。
掃描對象以後,咱們就能夠將未標記的對象刪除了。
刪除有三種方式,第一種方式是正常刪除。可是正常刪除會致使內存碎片的產生。因此第二種方式就是刪除以後進行壓縮,以減小內存碎片。還有一種方式叫作刪除拷貝,也就是說將alive的對象拷貝到新的內存區域,這樣一樣能夠解決內存碎片的問題。
在講CMS以前,咱們先講一下垃圾回收器中的Eden,Old和Survivor space幾個你們應該都很熟悉的分代技術。
Young Gen被劃分爲1個Eden Space和2個Suvivor Space。當對象剛剛被建立的時候,是放在Eden space。垃圾回收的時候,會掃描Eden Space和一個Suvivor Space。若是在垃圾回收的時候發現Eden Space中的對象仍然有效,則會將其複製到另一個Suvivor Space。
就這樣不斷的掃描,最後通過屢次掃描發現任然有效的對象會被放入Old Gen表示其生命週期比較長,能夠減小垃圾回收時間。
以後要將的幾個垃圾回收器,除了ZGC,其餘都使用的是分代的技術。
好了,如今繼續講CMS,CMS是mark and swap的升級版本,它使用多個線程來對heap區域進行掃描,從而提高效率。
CMS在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是mark-sweep。
使用CMS的命令很簡單:
-XX:+UseConcMarkSweepGC
上面是列出的一些CMS的調優參數。
Serial garbage collection使用單一的線程來進行垃圾回收操做,其好處就是不須要和其餘的線程進行交互。若是你是單核的CPU,那麼最好就是選擇Serial garbage collection,由於你不能充分利用多核的好處。一樣的它也經常用在比較小型的項目中。
Serial garbage collection在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。
下面是開啓命令:
-XX:+UseSerialGC
和serial GC相似,它在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。不一樣的是它是並行的。
能夠經過下面的命令來指定併發的線程:
-XX:ParallelGCThreads=N
若是你是多核處理器,那麼Parallel GC多是你的選擇。
Parallel GC是JDK8中的默認GC。而在JDK9以後, G1是默認的GC。
使用下面的命令來開啓Parallel GC:
-XX:+UseParallelGC
爲何叫G1呢,G1=Garbage First,它是爲替換CMS而生的,最先出如今java7中。
G1將heap區域劃分紅爲多個更小的區域,每一個小區域都被標記成爲young generation 或者old generation。從而運行GC在更小的範圍裏運行,而不是影響整個heap區域。
可使用下面的命令來開啓:
-XX:+UseG1GC
ZGC是一個可擴展的,低延遲的GC。ZGC是併發的,並且不須要中止正在運行的線程。
使用下面的命令來開啓:
-XX:+UseZGC
ZGC是在JDK11中被引入的。
小師妹:F師兄,你講了這麼多個GC,到底我該用哪一個呢?
高射炮不能用來打蚊子,因此選擇合適的GC纔是最終要的。這裏F師兄給你幾個建議:
本文介紹了幾種GC的算法,你們能夠根據須要選用。
本文做者:flydean程序那些事本文連接:http://www.flydean.com/jvm-gc-algorithms/
本文來源:flydean的博客
歡迎關注個人公衆號:程序那些事,更多精彩等着您!