【Java】GC and GC Tuning

目錄

1、什麼是垃圾

2、如何定位垃圾

reference count

Root Searching

3、常見的垃圾回收算法

Mark-Sweep(標記清除)

Copying(拷貝算法)

Mark-Compact(標記壓縮)

4、JVM內存分代模型(用於分代垃圾回收算法)

堆內存邏輯分區

5、垃圾回收器

Serial

Parallel Scavenge

ParNew

SerialOld

Parallel Old

CMS

6、JVM參數

7、思維導圖


1、什麼是垃圾

內存分配與回收方式:

  • C語言:malloc、free

  • C++:new、delete

  • Java:new  自動回收內存

自動回收內存系統不容易出錯,手動回收內存,容易出現以下的錯誤:

  • 忘記回收

  • 多次回收

垃圾的定義:沒有任何引用指向的一個對象或者多個對象(循環引用)。

當把成員變量設置爲空(null)之後,不再指向任何引用對象,那麼該對象就被稱作垃圾:

還有一種情況,多個對象之間互相引用,但是沒有其他的引用指向這個循環的對象。  

2、如何定位垃圾

  • reference count

當引用計數變爲0的時候,這個對象就成爲垃圾了。但是引用計數不能解決對象循環引用

如下:

每個引用計數都是1,但是它們全部是垃圾,所以用引用計數的方式的話,這些垃圾就找不到了,會發生內存泄漏。

  • Root Searching

根可達或者根搜索算法。

通過程序找到一些根對象,通過根對象找到它所連接的那些對象不是垃圾,其他的都是垃圾。

Roots:線程棧變量、靜態變量、常量池、JNI指針

3、常見的垃圾回收算法

  • Mark-Sweep(標記清除)

將可回收的對象標記爲非垃圾。

缺點:位置不連續,產生內存碎片。

  • Copying(拷貝算法)

內存一分爲二,將存活對象複製到未使用的內存中,原內存全部標記爲可使用;新分配內存時先分配存活對象所在的那段內存,垃圾回收時,重複上述操作。

特點:沒有碎片,但是內浪費空間。最大的問題:內存浪費。

  • Mark-Compact(標記壓縮)

將存活對象依次複製到垃圾對象和未使用的區域中,結合了標記清除和拷貝的做法,但是效率比copy略低。

三種方法找垃圾的效率是一致的,區別在於找到垃圾後對其進行整理的方式。拷貝算法是內存拷貝,是線性地址的拷貝,速度很快的,效率很高。但是壓縮算法卻不這麼簡單,因爲任意一個內存進行移動時,如果是多線程, 都要進行線程同步;如果是單線程,那單線程的效率本來就低。 所以任何一塊內存挪動都要進行線程同步,所以效率肯定是很低的。

4、JVM內存分代模型(用於分代垃圾回收算法)

目前,生產環境中普遍使用的是JDK1.7或JDK1.8,根據JDK版本不同,分代也不同。

JVM中分代:新生代+老年代+永久代(JDK1.7)/元數據區(JDK1.8)Metaspace。

  • 永久代和元數據區是裝載Class的,將硬盤上的Class對象load到內存的時候,裝載了永久代或者元數據區域,具體放在哪裏區別於使用的JDK版本
  • 永久代必須指定大小限制,而元數據可以設置,也可不設置,無上限(受限於物理內存)
  • 字符串常量在JDK1.7中,是放在永久代區域;而JDK1.8中,是放在堆裏
  • MethodArea是一個邏輯概念,並不是指的一個區域,在JDK1.7中對應的就是永久代,JDK1.8中對應的是元數據

堆內存邏輯分區

  • 新生代中分了兩類區域,eden和survivor,而survivor有兩塊。默認的比例,新生代:老年代=1:3,新生代中eden: survivor:survivor = 8:1:1。

之所以新生代中按照這個比例分配,是因爲eden區在GC的時候,90%的對象都會被回收,剩下的存活對象在survivor區是可以放下的。

當創建一個對象時,默認會去找eden區, 如果對象特別大,eden區裝不下則直接進入老年代。

新生代 = Eden + 2個survivor區(survivor0、survivor1)

  • YGC(Young GC)回收後,大多數的對象會被回收,活着的對象進入survivor0
  • 再次YGC,活着的對象eden+s0拷貝到s1,將eden和s0清空
  • 再次YGC,活着的對象eden+s1拷貝到s0,將eden和s1清空
  • 年齡足夠->老年代(年齡足夠:15,CMS 6)
  • survivor區裝不下的時候,裝不下的部分直接進入老年代

老年代:

  • 頑固份子
  • 老年代區域滿了,就進行Full GC(簡稱FGC, FGC包括新生代和老年代同時GC)

GC Tuning:儘量減少FGC。

5、垃圾回收器

  • Serial、ParNew、Parallel Scavenge是用於回收Young Generation
  • CMS、Serial Old、Parallel Old是用於回收Old Generation
  • G1、ZGC、Shenandoah不區分老年代和新生代。
  • Epsilon是一個空的GC,僅僅用於調試JDK。
  • 圖中的紅色虛線表示可以配合使用。

Serial

垃圾回收的時候,程序是無法執行的。stop-the-world(STW)是停止程序運行,回收線程開始運行,回收結束後程序再接着運行。

Parallel Scavenge

並行回收,多個線程同時進行垃圾回收。

ParNew

配合CMS的年輕代並行回收。

SerialOld

單線程回收算法用於old區域

Parallel Old

多線程回收算法用於old區域

CMS

ConcurrentMarkSweep,用於回收老年代,在垃圾回收的同時程序也能運行。(黃色的表示垃圾回收線程,藍色表示程序執行線程)

調優針對的是Serial、Parallel Scavenge和Serial Old、Parallel New,因爲JDK1.8默認的垃圾回收:Parallel Scavenge + Parallel Old。

6、JVM參數

JVM的命令行參數參考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

JVM參數分類:

  • 標準:-開頭,所有的HotSpot都支持。 如:java -version
  • 非標準:-X開頭,特定版本HotSpot支持特定命令
  • 不穩定:-XX開頭,下個版本可能取消
    1. -XX: +PrintFlagsFinal   --- 設置值(最終生效值)
    2. -XX:+PrintFlagsInitial  --- 默認值
    3. -XX:+PrintCommandLineFlags ---命令行參數

7、思維導圖

參考文檔:

https://blogs.oracle.com/jonthecollector/our-collectors

https://blogs.oracle.com/jonthecollector/why-not-a-grand-unified-garbage-collector

發佈了154 篇原創文章 · 獲贊 48 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章