Java功底 — 掌握JVM這一篇文章就夠了

寫作這篇文章的目的,是想以淺顯易懂的描述讓大家重新認識JVM,主要包含下面幾個方面

  • JVM架構邏輯
  • JVM堆內存結構模型
  • JVM垃圾回收機制/常用垃圾回收器(Garbage Collector)
  • JVM調優

1.JVM架構邏輯

這張圖描繪了JVM和系統調用之間的關係。

  1. 開始是將編譯好的字節碼文件,經過類加載器,將我們的字節碼加載到內存當中,就生成了Class對象。
  2. 綠色表示的方法區和堆區是所有線程共享的內存區域。
  3. 而沙棕色表示的java棧、本地方法棧和程序計數器是運行時線程私有的內存區域,相互獨立開。
  • 方法區(Method Area):存放類信息、常量、靜態變量
  • 堆(Heap):存放對象實例和數組

Java堆是jvm裏內存最大的一塊,幾乎所有的實例對象都是在這裏分配內存,同時堆也是垃圾收集器管理的主要區域,後面有詳細描述堆內存的結構。

當方法區和堆區無法滿足內存分配需求,也無法再擴展時,就會拋出我們最不想要的OutOfMemoryError異常。

 

2.怎麼找到垃圾?

JVM怎麼知道堆裏的對象是垃圾呢,有兩種方式:

  • 引用計數法

每個對象有一個引用計數屬性,新增一個引用時計數加1,釋放引用時計數減1,計數爲0時標記爲可以回收。

這個方法雖然簡單,但無法解決對象相互引用的問題。

  • 根查找/根可達(Root Searching)

從GC Roots開始向下搜索,搜索走過的路徑稱爲引用鏈。到一個對象沒有引用鏈相連時,則根不可達,該對象不可用標記爲垃圾。

畫個簡單的示意圖,像這樣:

順着線一直能找到的就不是垃圾,線斷的(根不可達)的是垃圾。

3.垃圾回收算法

垃圾回收算法,一直以來就三種,不同垃圾回收器只是用的算法不一樣。

  • Mark-Sweep(標記清除)
  • Copyting(複製)
  • Mark-Compact(標記整理)

Mark-Sweep(標記清除)

在一塊內存中,將要回收的數據,標記直接清除。

壞處:碎片化,清除後內存一個洞一個洞的。

Copyting(複製)

把內存線分爲兩半,每次只允許用一半。進行垃圾回收的時候,將一半內存中的存活的數據直接複製到另一塊內存中,然後清理掉之前的內存區域。

效率是最高的,但會造成內存浪費。

Mark-Compact(標記整理)

將內存中的對象,標記集中移動到內存的一邊進行清理,把有用的對象落到前面去(整理)。

效率比Copy略低,好處是把空間讓出來了,後面的大對象可以直接在後面分配內存。

4.常用的垃圾回收器有哪些?

算法是GC的策略,而垃圾收集器,就是根據垃圾回收算法來進行垃圾處理的具體實現。

到現在爲止,垃圾回收器一共有10種,如下圖所示:

生產用的最多的是jdk1.8,默認是Parallel和Parallel Old。

  •  Serial

Serial(單線程)垃圾收集器是最基本、發展歷史最悠久的收集器。

垃圾回收時,stop-the-word 停掉所有工作線程(幾十毫秒),採用Copy複製算法。

  • Serial Old

Serial Old(單線程)是應用在老年代的垃圾收集器,算法是Mark-Compact 標記整理算法。

  • ParNew

ParNew垃圾收集器是Serial收集器的多線程版本,實現算法和Serial一樣是Copy複製算法。

  • Parallel Scavenge

Parallel Scavenge與前面兩種新生代的收集器相比,不可以和CMS組合使用,使用算法也是Copy複製算法。它與前兩種最大的區別是,他關注的是吞吐量而不是延遲。

  • Parallel Old

Parallel Old是Serial Old的多線程版本,應用在老年代的收集器,實現算法和Serial Old一樣是Mark-Compact 標記整理算法。

  • CMS

Concurrent Mark Sweep,併發標記清理。在線程執行過程中也可也進行垃圾收集。分爲四個過程:1.初始標記 2.併發標記 3.重新標記 4.併發清理

  • G1(Garbage First)

JDK7就已加入JVM的收集器大家庭中,成爲HotSpot重點發展的垃圾回收技術。同優秀的CMS垃圾回收器一樣,G1也是關注最小時延的垃圾回收器,也同樣適合大尺寸堆內存的垃圾收集,官方也推薦使用G1來代替選擇CMS。G1最大的特點是引入分區的思路,弱化了分代的概念,合理利用垃圾收集各個週期的資源,解決了其他收集器甚至CMS的衆多缺陷。

G1採用的標記整理算法。

  • ZGC

zgc是jdk11中要發佈的最新垃圾收集器。完全沒有分代的概念,先說下它的優點吧,官方給出的是無碎片,時間可控,超大堆。

  • Shenandoah

Shenandoah是一款concurrent及parallel的垃圾收集器,跟ZGC一樣也是面向low-pause-time的垃圾收集器,不過ZGC是基於colored pointers來實現,而Shenandoah GC是基於brooks pointers來實現的。

  • Epsilon

Java11新的Epsilon垃圾收集器。

Epsilon(A No-Op Garbage Collector)垃圾回收器控制內存分配,但是不執行任何垃圾回收工作。一旦堆被耗盡,JVM就直接關閉。設計的目的是提供一個完全消極的GC實現,分配有限的內存分配,最大限度降低消費內存佔用量和內存吞吐時的延遲時間。

5.GC的演化

其實垃圾回收的演化,是隨着現在設備內存大小的增進而演進的。

  • 最初幾兆-幾十兆

Serial單線程STW垃圾回收。

  • 幾十兆-上百兆1G

Parallel 並行多線程進行垃圾回收,但線程不是越多越好,有線程切換耗時。

  • 幾G-幾十G到上T

Concurrent GC

6.堆內存邏輯分區

在目前1.8中用的最多的,叫分代管理的辦法。

畫了個堆內存邏輯分區的示意圖,如下:

無論內存多大,將內存一分爲二。默認新生代和老年代的比例是1:2。新生代比例8:1:1,這個新生代的比例,是根據統計學結果得出的,因爲在YGC能回收90%的垃圾。新生代使用算法Copying複製算法,老年代使用的Mark Compact標記整理算法。

到一個對象誕生的時候,先放在新生代,當一個對象被清理了很多次都沒被回收就會放到老年代。不同回收收集器都不一樣,一般是15或6次。

JVM調優主要就是通過參數調比例。

7.垃圾回收過程

  • 新生代回收 Young GC (YGC)
  • 老年代回收 Old GC (OGC)
  • 當YGC和OGC都發生,就叫Full GC

垃圾回收過程如下:

1.新誕生的對象放在Eden(伊甸園)區

2.當伊甸園區滿了,會清理垃圾,把活着的對象複製到survivor(倖存者區),然後把整個Eden清空,這就是一個完整的YGC。

3.如果再來一次,則把Eden活着的和survivor的對象複製到survivor2,同時把Eden和survivor1清空內存,2個survivor中始終保證有一個是空的。

8.JVM調優

JVM調優其實就是通過參數控制內存大小,優化垃圾回收過程。

如下圖所示:

最後彙總一下JVM常用參數配置:

類加載設置

  • -XX:+TraceClassLoading      類加載日誌
  • -XX:+TraceClassUnloading  類卸載日誌

堆設置

 

  • -Xms  初始堆大小
  • -Xmx  最大堆大小
  • -XX:NewSize=n  設置年輕代大小
  • -XX:NewRatio=n  設置年輕代和年老代的比值。如:爲3,表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代和的1/4
  • -XX:SurvivorRatio=n  年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5
  • -XX:MaxPermSize=n  設置持久代大小

收集器設置

 

  • -XX:+UseSerialGC  設置串行收集器
  • -XX:+UseParallelGC  設置並行收集器
  • -XX:+UseParalledlOldGC  設置並行年老代收集
  • -XX:+UseConcMarkSweepGC  設置併發收集器

並行收集器設置

 

  • -XX:ParallelGCThreads=n  設置並行收集器收集時使用的並行收集線程數
  • -XX:MaxGCPauseMillis=n  設置並行收集最大暫停時間
  • -XX:GCTimeRatio=n  設置垃圾回收時間佔程序運行時間的百分比,公式爲1/(1+n)

併發收集器設置

  • -XX:+CMSIncrementalMode  設置爲增量模式,適用於單CPU
  • -XX:ParallelGCThreads=n  設置併發收集器年輕代收集方式爲並行收集時,使用的收集線程數

垃圾回收統計信息

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -Xloggc:filename

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章