天天看點

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法

前言介紹:一個項目的完美運作,是離不開jvm的優化的,對于一些有經驗的程式員來說,由于每個項目的差異,會根據項目的加載情況,做參數調整以及垃圾回收器的選擇,下面介紹了一些參數的優化及垃圾回收器的選擇和算法介紹,供自己以後參考

一,Java虛拟機基本結構:

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 類加載子系統:  負責從檔案系統或者網絡中加載Class資訊
  • 方法區:  有時也稱為永久區, 用于存放加載的類資訊, 以及存放運作時常量池(包括字元串和數字常量, 這部份常量資訊是Class檔案中常量池部分的記憶體映射), java8開始稱為中繼資料區, 設定參數也有所不一樣
  • Java堆:  虛拟機啟動時建立, 是Java程式最主要的記憶體工作區域, 幾乎所有對象都會存放在Java堆中, 堆是所有線程共享的
  • 直接記憶體:  Java的NIO庫允許Java程式使用直接記憶體(例: DirectByteBuffer, 每次建立和釋放都要調用System.gc()), 直接記憶體是Java堆外直接向系統申請的記憶體區間, 直接記憶體的通路優于堆,直接記憶體不受限于xmx的值, 不過受限于系統的最大記憶體
  • 垃圾回收系統:  可以對Java堆,直接記憶體和方法區進行回收, 垃圾回收是隐式的自動完成的, 不用程式手動是釋放記憶體
  • Java棧:  每個虛拟機線程都有私有的Java棧, 一個線程的棧是線上程建立時建立的, 棧中儲存幀, 局部變量, 方法參數 , -Xss用于指定Java棧的空間大小
  • 本地方法棧:  類似Java棧, 用于本地方法的調用, 是Java虛拟機的重要擴充, 即JNI, Java本地接口的調用, 通常是C或C++
  • PC寄存器:  也被為程式計數器, 是每個線程私有的空間, Java線程正在執行的方法如果不是本地方法, PC寄存器就會指向目前正在被執行的指令, 反之, PC寄存器的值是undefined
  • 執行引擎:  Java虛拟核心元件之一, 負責執行虛拟機的位元組碼, 現代虛拟機的即時編譯會将方法編譯成機器碼後再執行, 會提高執行效率

二,Java虛拟機參數設定:

  • 性能參數:
-server 
      以server模式運作時将擁有:更大、更高的并發處理能力,更快更強捷的JVM垃圾回收機制,可以獲得更多的負載與吞吐量

    -Xmx 
      指定java程式的最大堆記憶體, 使用java -Xmx5000M -version判斷目前系統能配置設定的最大堆記憶體

    -Xms 
      指定最小堆記憶體, 通常設定成跟最大堆記憶體一樣,減少GC

    -Xmn 
      設定年輕代大小為512m。整個堆大小=年輕代大小 + 年老代大小。是以增大年輕代後,将會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。

    -Xss 
      指定線程的最大棧空間, 此參數決定了java函數調用的深度, 值越大調用深度越深, 若值太小則容易出棧溢出錯誤(StackOverflowError) 
    設定每個線程的堆棧大小。這個就要依據你的程式,看一個線程 大約需要占用多少記憶體,可能會有多少線程同時運作等。一般不易設定超過1M,要不然容易出現out ofmemory

    -XX:PermSize 
      指定方法區(永久區)的初始值,預設是實體記憶體的1/64, 在Java8永久區移除, 代之的是中繼資料區, 由-XX:MetaspaceSize指定

    -XX:MaxPermSize 
      指定方法區的最大值, 預設是實體記憶體的1/4, 在java8中由-XX:MaxMetaspaceSize指定中繼資料區的大小

    -XX:NewRatio=n 
      年老代與年輕代的比值,-XX:NewRatio=2, 表示年老代與年輕代的比值為2:1

    -XX:SurvivorRatio=n 
      Eden區與Survivor區的大小比值,-XX:SurvivorRatio=8表示Eden區與Survivor區的大小比值是8:1:1,因為Survivor區有兩個(from, to)

    -XX:+DoEscapeAnalysis 
      開啟逃逸分析, 逃逸分析的目的是判斷對象的作用域是否可能逃逸出函數體, 逃逸分析是棧上配置設定的技術基礎,對于非逃逸對象而言就是一個局部變量, 而對象未發生逃逸時, 虛拟機就有可能進行線上配置設定, 不是堆上, 棧上配置設定速度快,并且能避免垃圾回收帶來的負面影響, 棧上配置設定是虛拟機提供的很好的對象配置設定優化政策

    -XX:+EliminateAllocations 
      開啟标量替換(預設打開), 即允許對象打散配置設定在棧上, 即對象的屬性視為獨立局部變量進行配置設定到棧上

    -XX:+UseCompressedOops 
      開啟指針壓縮

    -XX:+AggressiveOpts 
      啟用JVM開發團隊最新的調優成果。例如編譯優化,偏向鎖,并行年老代收集等 
       
    -XX:-UseTLAB 
      關閉TLAB , 預設是打開的

    -Djava.awt.headless=true 
      有時我們會在我們的J2EE工程中使用一些圖表工具如:jfreechart,用于在web網頁輸出GIF/JPG等流,在winodws環境下,一般我 們的app server在輸出圖形時不會碰到什麼問題,但是在linux/unix環境下經常會碰到一個exception導緻你在winodws開發環境下圖檔顯 示的好好可是在linux/unix下卻顯示不出來,是以加上這個參數以免避這樣的情況出現.

    -XX:+DisableExplicitGC 
      在程式代碼中不允許有顯示的調用”System.gc()”。看到過有兩個極品工程中每次在DAO操作結束時手動調用System.gc()一下,覺得這 樣做好像能夠解決它們的out ofmemory問題一樣,付出的代價就是系統響應時間嚴重降低,就和我在關于Xms,Xmx裡的解釋的原理一樣,這樣去調用GC導緻系統的JVM大起大 落,性能不到什麼地方去喲!

    -XX:+UseBiasedLocking 
      偏向鎖,啟用一個優化了的線程鎖,我們知道在我們的appserver,每個http請求就是一個線程,有的請求短有的請求長,就會有請求排隊的現象,甚至還會出現線程阻塞,這個優化了的線程鎖使得你的appserver内對線程處理自動進行最優調配。
           
  • 調試參數:主要是列印jvm的一些日志
-verbose:gc 
  表示輸出虛拟機中GC的詳細情況

-Xloggc: 
  指定gc以檔案輸出(預設在控制台), 後面跟日志檔案路徑

-XX:+PrintGC 
  加上參數可以在輸出日志中可以檢視垃圾回收前後堆的大小, 即列印gc日志

-XX:+PrintGCDetails 
  列印gc日志的更加詳細的資訊

-XX:+PrintHeapAtGC 
  列印每次GC前後堆的資訊

-XX:+PrintGCTimeStamps 
  輸出GC發生時,gc發生的時間

-XX:+PrintGCApplicationConcurrentTime 
  列印應用程式的執行時間

-XX:+PrintGCApplicationStoppedTime 
  可以列印應用程式由于GC而産生的停頓時間

-XX:+PrintReferenceGC 
  跟蹤系統内的軟引用,弱引用,虛引用和Finallize隊列

-XX:+TraceClassLoading 
  列印類加載時的相關資訊 
           

三, Java堆:

  • 堆記憶體(heap)和 Permanent區。
()Java堆記憶體(heap): 
  –是 JVM 用于配置設定 Java 對象的記憶體,包含活動對象和不可用對象 
  –堆大小通常是在伺服器啟動時使用 java 指令中的 –Xms(最小) –Xmx(最大)标志來定義。

()Permanent區:(永久區) 
  –指記憶體的永久儲存區域 
  –是Sun JDK和HP JDK用來加載類(class)和Meta資訊的專門的記憶體區 
  –這個區域不歸屬Java堆記憶體(heap)範圍 
  –Class在被Loader時就會被放到此,如果Java應用很大,例如類(class)很多,那麼建議增大這個區域的大小來滿足加載這些類的記憶體需求 
  –通過–XX:PermSize=***M –XX:MaxPermSize=***M調整
           
  • Java堆的結構:(新生代+老年代)
    JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
    JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
如上圖, 如果 -Xms指定的值比-Xmx的小,那麼兩者的內插補點就是Virtual記憶體值。随着程式的運作,Eden區、 Tenured區和Perm區會逐漸使用保留的Virtual空間。

    Young Generation 新生代, 即圖中的Eden + From Space + To Space
    Eden 存放新生的對象
    Survivor Space 有兩個,存放每次垃圾回收後存活的對象
    Old Generation Tenured Generation 老年代, 即圖中的Old Space 主要存放應用程式中生命周期長的存活對象
           

四, 垃圾回收器:

1, 串行回收器

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 新生代串行回收器
(1)特點: 
  –它僅僅使用單線程進行垃圾回收 
  –它是獨占式的垃圾回收 
  –進行垃圾回收時, Java應用程式中的線程都需要暫停(Stop-The-World) 
  –使用複制算法 
  –适合CPU等硬體不是很好的場合 
(2)設定參數: 
  -XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器, 當以client模式運作時, 它是預設的垃圾收集器
           
  • 老年代串行回收器
(1)特點: 
  –同新生代串行回收器一樣, 單線程, 獨占式的垃圾回收器 
  –通常老年代垃圾回收比新生代回收要更長時間, 是以可能會使應用程式停頓較長時間 
(2)設定參數: 
  -XX:+UseSerialGC 新生代, 老年代都使用串行回收器 
  -XX:+UseParNeGC 新生代使用ParNew回收器, 老年代使用串行回收器 
  -XX:+UseParallelGC 新生代使用ParallelGC回收器, 老年代使用串行回收器
           

2, 并行回收器

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 新生代ParNew回收器
(1)特點: 
  –将串行回收多線程化, 
  –使用複制算法 
  –垃圾回收時, 應用程式仍會暫停, 隻不過由于是多線程回收, 在多核CPU上,回收效率會高于串行回收器, 反之在單核CPU, 效率會不如串行回收器 
(2)設定參數: 
  -XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器 
  -XX:+UseConcMarkSweepGC 新生代使用ParNew回收器, 老年代使用CMS回收器 
  -XX:ParallelGCThreads=n 指回ParNew回收器工作時的線程數量, cpu核數小時時, 其值等于cpu數量, 高于時,可以使用公式(+((*CPU_count)/))
           
  • 新生代ParallelGC回收器
(1)特點: 
  –同ParNew回收器一樣, 不同的地方在于,它非常關注系統的吞吐量(通過參數控制) 
  –使用複制算法 
  –支援自适應的GC調節政策

(2)設定參數:
  -XX:+UseParallelGC  新生代用ParallelGC回收器, 老年代使用串行回收器 
  -XX:+UseParallelOldGC  新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器系統吞吐量的控制: 
  -XX:MaxGCPauseMillis=n(機關ms)   設定垃圾回收的最大停頓時間, 
  -XX:GCTimeRatio=n(n在-之間)  設定吞吐量的大小, 假設值為n, 那系統将花費不超過/(n+)的時間用于垃圾回收 
  -XX:+UseAdaptiveSizePolicy  打開自适應GC政策, 在這種模式下, 新生代的大小, eden,survivior的比例, 晉升老年代的對象年齡等參數會被自動調整,以達到堆大小, 吞吐量, 停頓時間之間的平衡點
           
  • 老年代ParallelOldGC回收器
(1)特點: 
  –同新生代的ParallelGC回收器一樣, 是屬于老年代的關注吞吐量的多線程并發回收器 
  –使用标記壓縮算法, 
(2)設定參數: 
  -XX:+UseParallelOldGC  新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器, 是非常關注系統吞吐量的回收器組合, 适合用于對吞吐量要求較高的系統 
  -XX:ParallelGCThreads=n   指回ParNew回收器工作時的線程數量, cpu核數小時時, 其值等于cpu數量, 高于時, 可以使用公式(+((*CPU_count)/))
           

3, CMS回收器(Concurrent Mark Sweep,并發标記清除)

  • 老年代的并發回收器
(1)特點: 
  –是并發回收, 非獨占式的回收器, 大部分時候應用程式不會停止運作 
  –針對年老代的回收器, 
  –使用并發标記清除算法, 是以回收後會有記憶體碎片, 可以使參數設定進行記憶體碎片的壓縮整理 
  –與ParallelGC和ParallelOldGC不同, CMS主要關注系統停頓時間 
(2)CMS主要步驟: 
  1. 初始标記 
  2. 并發标記 
  3. 預清理 
  4. 重新标記 
  5. 并發清理 
  6. 并發重置

–>注:初始标記與理新标記是獨占系統資源的,不能與使用者線程一起執行,而其它階段則可以與使用者線程一起執行 
(3)設定參數: 
  -XX:-CMSPrecleaningEnabled  關閉預清理, 不進行預清理, 預設在并發标記後, 會有一個預清理的操作,可減少停頓時間 
  -XX:+UseConcMarkSweepGC  老年代使用CMS回收器, 新生代使用ParNew回收器 
  -XX:ConcGCThreads=n  設定并發線程數量, 
  -XX:ParallelCMSThreads=n  同上, 設定并發線程數量, 
  -XX:CMSInitiatingOccupancyFraction=n  指定老年代回收閥值, 即當老年代記憶體使用率達到這個值時, 會執行一次CMS回收,預設值為, 設定技巧: (Xmx-Xmn)*(-CMSInitiatingOccupancyFraction)/)>=Xmn 
  -XX:+UseCMSCompactAtFullCollection  開啟記憶體碎片的整理, 即當CMS垃圾回收完成後, 進行一次記憶體碎片整理, 要注意記憶體碎片的整理并不是并發進行的, 是以可能會引起程式停頓 
  -XX:CMSFullGCsBeforeCompation=n  用于指定進行多少次CMS回收後, 再進行一次記憶體壓縮 
  -XX:+CMSParallelRemarkEnabled  在使用UseParNewGC 的情況下, 盡量減少 mark 的時間 
  -XX:+UseCMSInitiatingOccupancyOnly  表示隻有達到閥值時才進行CMS回收
           
  • Class的回收(永久區的回收)
設定參數: 
  -XX:+CMSClassUnloadingEnabled  開啟回收Perm區的記憶體, 預設情況下, 是需要觸發一次FullGC 
  -XX:CMSInitiatingPermOccupancyFraction=n  當永久區占用率達到這個n值時,啟動CMS回收, 需上一個參數開啟的情況下使用
           

4, G1回收器(jdk1.7後全新的回收器, 用于取代CMS)

(1)特點: 
  –獨特的垃圾回收政策, 屬于分代垃圾回收器, 
  –使用分區算法, 不要求eden, 年輕代或老年代的空間都連續 
  –并行性: 回收期間, 可由多個線程同時工作, 有效利用多核cpu資源 
  –并發性: 與應用程式可交替執行, 部分工作可以和應用程式同時執行, 
  –分代GC: 分代收集器, 同時兼顧年輕代和老年代 
  –空間整理: 回收過程中, 會進行适當對象移動, 減少空間碎片 
  –可預見性: G1可選取部分區域進行回收, 可以縮小回收範圍, 減少全局停頓 
(2)G1的收集過程 
1. 新生代GC: 
2. 并發标記周期: 
  –初始标記新生代GC(此時是并行, 應用程式會暫停止)–>根區域掃描–>并發标記–>重新标記(此時是并行, 應用程式會暫停止)–>獨占清理(此時應用程式會暫停止)–>并發清理 
3. 混合回收: 
  –這個階段即會執行正常的年輕代gc, 也會選取一些被标記的老年代區域進行回收, 同時處理新生代和年老輕 
4. 若需要, 會進行FullGC: 
  –混合GC時發生空間不足 
  –在新生代GC時, survivor區和老年代無法容納幸存對象時, 
  –以上兩者都會導緻一次FullGC産生 
(3)設定參數: 
  -XX:+UseG1GC  打開G1收集器開關, 
  -XX:MaxGCPauseMillis=n  指定目标的最大停頓時間,任何一次停頓時間超過這個值, G1就會嘗試調整新生代和老年代的比例, 調整堆大小, 調整晉升年齡 
  -XX:ParallelGCThreads=n  用于設定并行回收時, GC的工作線程數量 
  -XX:InitiatingHeapOccpancyPercent=n  指定整個堆的使用率達到多少時, 執行一次并發标記周期, 預設, 過大會導緻并發标記周期遲遲不能啟動, 增加FullGC的可能, 過小會導緻GC頻繁, 會導緻應用程式性能有所下降
           

5, 其他GC相關的設定

  • System.gc()
(1)禁用System.gc() 
  -XX:+DisableExplicitGC  禁止程式中調用System.gc(), 加了此參數, 程式若有調用, 傳回的空函數調用 
   System.gc()的調用, 會使用FullGC的方式回收整個堆而會忽略CMS或G1等相關回收器 
(2)System.gc()使用并發回收 
  -XX:+ExplicitGCCinvokesConcurrent   使用并發方式處理顯示的gc, 即開啟後, System.gc()這種顯示GC才會并發的回收, (CMS, G1)
           
  • 并行GC前額外觸發的新生代GC
(1)使用并行回收器(UseParallelGC或者UseParallelOldGC)時, 會額外先觸發一個新生代GC, 目的是盡可能減少停頓時間 
(2)若不需要這種特性, 可以使用以下參數去除 
  -XX:-ScavengeBeforeFullGC   即去除在FullGC之前的那次新生代GC, 原本預設值為true 
           
  • 對象何時進入老年代
(1)當對象首次建立時, 會放在新生代的eden區, 若沒有GC的介入,會一直在eden區, GC後,是可能進入survivor區或者年老代 
(2)當對象年齡達到一定的大小 ,就會離開年輕代, 進入老年代, 對象進入老年代的事件稱為晉升, 而對象的年齡是由GC的次數決定的, 每一次GC,若對象沒有被回收, 則對象的年齡就會加1, 可以使用以下參數來控制新生代對象的最大年齡: 
  -XX:MaxTenuringThreshold=n  假設值為n , 則新生代的對象最多經曆n次GC, 就能晉升到老年代, 但這個必不是晉升的必要條件 
  -XX:TargetSurvivorRatio=n  用于設定Survivor區的目标使用率,即當survivor區GC後使用率超過這個值, 就可能會使用較小的年齡作為晉升年齡 
(3)除年齡外, 對象體積也會影響對象的晉升的, 若對象體積太大, 新生代無法容納這個對象, 則這個對象可能就會直接晉升至老年代, 可通過以下參數使用對象直接晉升至老年代的門檻值, 機關是byte 
  -XX:PretenureSizeThreshold  即對象的大小大于此值, 就會繞過新生代, 直接在老年代配置設定, 此參數隻對串行回收器以及ParNew回收有效, 而對ParallelGC回收器無效,
           
  • 在TLAB上配置設定對象(Thread Local Allocation Buffer, 線程本地配置設定緩存)
(1)TLAB: TLAB是一個線程專用的記憶體配置設定區域, 虛拟機為線程配置設定空間, 針對于體積不大的對象, 會優先使用TLAB, 這個可以加速對象的配置設定, TLAB是預設開啟的, 若要關閉可以使用以下參數關閉 
  -XX:-UseTLAB  關閉TLAB 
  -XX:+UseTLAB  開啟TLAB, 預設也是開啟的 
  -XX:+PrintTLAB  觀察TALB的使用情況 
  -XX:TLABRefillWasteFraction=n  設定一個比率n, 而refill_waste的值就是(TLAB_SIZE/n), 即TLAB空間較小, 大對象無法配置設定在TLAB,是以會直接配置設定到堆上,TLAB較小也很容易裝滿, 是以當TLAB的空間不夠配置設定一個新對象, 就會考慮廢棄目前TLAB空間還是直接配置設定到堆上, 就會使用此參數進行判斷, 小于refill_waste就允許廢棄, 而建立TLAB來配置設定對象,而大于refill_waste就直接在堆上配置設定, 預設是 
  -XX:+ResizeTLAB  開啟TLAB自動調整大小, 預設是開啟的, 若要關閉把+号換成-号即可 
  -XX:TLABSize=n  設定一個TLAB的大小, 前提先關閉TLAB的自動調整
           

JVM垃圾回收算法

  • 1, 引用計數法:(Reference Counting)
原理:  即為每個對象配備一個整型的計數器, 任何一個對象引用A, 則A的計數器就加, 引用失效, 計數器減, 當對象A的計數器為時, 則對象就不可能再被使用了 
缺點: 
  ()無法處理循環引用的問題,是以Java垃圾回收器中, 沒有用這種算法 
  ()引用計數器在對象引用産生和消除時, 則會有增和減的問題, 對性能有一定的損耗
           
  • 2, 标記清除算法(Mark-Sweep)

[可達對象:  指通過根對象進行引用搜尋, 最終可以達到的對象]

[不可達對象:  指通過根對象進行引用搜尋, 最終沒有被引用到的對象]

原理:  分兩階段, 标記和清除, 首先通過根節點, 标記所有從根節點開始的可達對象, 未被标記的對象就是未被引用的垃圾對象, 而在清除階段就會清除所有未被标記的對象

缺點:

  (1)可能産生空間碎片, 回收後的空間可能是不連續的(在對象的堆中配置設定時, 尤其大對象配置設定時, 不連續的記憶體空間效率會低很多)

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 3, 複制算法(Copying)

原理: 将原有記憶體空間分兩塊, 每次隻使用其中一塊, 在垃圾回收時, 将正在使用的存活對象放入未使用的記憶體塊中, 之後清除正在用的記憶體塊的其他對象, 交換兩個 記憶體的角色, 完成垃圾回收, 适用于存活對象少, 垃圾對象多的情況

優點:  效率很高, 并且保證了配置設定的記憶體是連續的

缺點:  系統記憶體折半

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 4, 标記壓縮算法(Mark-Compact): (又名标記-整理算法)

原理:  基于标記清除算法的改進, 在标記結束後, 将存活對象壓縮至記憶體的一端, 再清除邊界外的所有空間. 相當于在标記清除算法後, 進行了一次記憶體碎片整理

優點:  避免了碎片的産生, 也不需要兩塊記憶體空間

JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 5, 分代算法(Generational Collecting)

    原理: 根據垃圾回收對象的特性, 選擇合适的算法回收

    JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法JVM性能參數優化及JVM垃圾回收器和JVM垃圾回收算法
  • 6, 分區算法(Region)

    原理: 将整個空間分成連續的不同小區間,每個小區間獨立使用, 獨立回收,

    優點: 可以控制一次回收多少個記憶體區間, 減少GC停頓

繼續閱讀