一次 JVM 調優的筆記
1. JVM Tuning基礎知識
1.1 Java堆結構
Java堆可以處於物理上不連續的記憶體空間上,只要邏輯上是連續的即可。Java堆就是各種物件分配和儲存的記憶體空間,執行緒間共享。Java堆分為Eden區,Survivor區,tenured區和Permanent區,如下圖所示。
Java堆的分配原則如下:
- Java堆分佈如下圖所示,新的類的例項大部分在Eden(之所以用Eden這個詞也就是表示初創起始的意思)區分配。
- Eden區滿的時候,或者需要GC時,依然存活的物件將被複制到Survivor區,一共有兩個survivor區,當一個survivor區滿時,依然存活的物件將被複制到另一個survivor區。
- 當survivor區全滿時,從第一個survivor區複製過來的,且此時還存活的物件將被複制到Tenured Generation(年老代)
- Perm Generation用於存放靜態檔案,持久代大小可以通過MaxPermSize進行設定。
Perm Generation是JVM的駐留記憶體,用於存放JDK自身攜帶的CLASS,Interface的後設資料等。被裝載進此區域的資料是不會被垃圾回收器GC回收掉的,關閉JVM時,釋放此區域所控制的記憶體。
Java物件的宣告週期在堆中從Young到Tenured,這個期間從Eden誕生到Tenured死亡。當EDEN區不夠用時,將觸發 minor gc,GC會對EDEN區進行垃圾回收,將不再使用的物件進行銷燬,同時如果發現物件還被其他物件引用,則將物件移動到survivor區,後面依此類 推,當Tenured區發生GC時,稱為major gc,由於java中大部分物件的生命週期都很短,所以GC一般發生在Eden區,因此minor gc發生的頻率比major gc要高很多。如果最後整個堆空間都滿了,則會爆出異常JVM對空間溢位:java.lang.OutOfMemoryError: java heap space。
1.2 JVM GC演算法列舉
Mark-sweep演算法
即標記回收演算法,將需要回收的物件標記,再統一回收。這種演算法適合Perm代的物件,以為Perm代的儲存空間比較大,需要回收的又不多。
Copying演算法
即複製演算法,直接拷貝。適合Eden區的物件向survivor區拷貝,因為Eden區的物件大部分都需要GC,而且Eden區的容量小。
Mark-compact演算法
即標記整理演算法,與標記清除演算法不同,標記整理演算法避免了記憶體碎片問題,它將所有存活的物件向記憶體的一端移動,“整理”完成後,一端是存活的物件一端是可回收記憶體,然後直接清除可回收記憶體。
1.3 GC收集器
Serial收集器
最基本、最古老的收集器。這是一個單執行緒收集器,GC過程中,應用會被停掉,即stop-the-world。
Parnew收集器
實現程式碼與serial差不多,不同的是這是一個多執行緒回收器。
Paralled scavenge收集器
Paralled scavenger收集器是一個新生代回收器,使用複製演算法,也是一個多執行緒並行收集器。parallel scavenge收集器專注於提高吞吐量,吞吐量定義為:
吞吐量 = 使用者程式執行時間/(使用者程式執行時間 + 垃圾回收時間)
Serial old收集器
Serial old是Serial收集器的老年代版本。
Parallel old收集器
Parallel Scavenge收集器的老年代版本。使用標記-整理演算法。
CMS(concurrent mark sweep)收集器:併發標記清除收集器,以最短停頓時間為目標的收集器。採用標記—清除演算法實現。
2.VisualVM1實際監控JVM狀態
2.1 常用調優引數列表
主要需要熟悉的是總儲存空間大小,各個區的比例設定,針對不同的區設定不同的回收器和回收演算法。在預設情況下有些選項是不開啟的,因此需要手動配置,並且根據自己應用的情況選擇適當的引數。
引數 | 描述 |
–XX:+UseSerialGC | 使用序列GC收集器 |
–XX:+UseParallelGC | 使用並行GC收集器 |
–XX:+UseParallelOldGC | 使用parallel old收集器 |
–XX:+UseConcMarkSweepGC | 使用CMS收集器 |
-Xms | 初始狀態堆大小 |
-Xmx | 堆的最大大小 |
-XX:MaxPermSize=n | 設定Permanent區的大小 |
-XX:NewSize=n | 設定Young generation大小 |
-XX:NewRatio=n | 設定年輕代和年老代的比值 |
2.2 檢視並分析GC日誌
對於應用程式,在eclipse中可以通過工程屬性增加VM屬性,將GC輸出儲存為日誌檔案。
GC輸出選項:
-XX:+PrintGC 輸出GC日誌 -XX:+PrintGCDetails 輸出GC的詳細日誌 -XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準時間的形式) -XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800) -XX:+PrintHeapAtGC 在進行GC的前後列印出堆的資訊 -Xloggc:../logs/gc.log 日誌檔案的輸出路徑
日誌輸出形式:
Java HotSpot(TM) Client VM (25.45-b02) for windows-x86 JRE (1.8.0_45-b14), built on Apr 10 2015 10:46:40 by "java_re" with MS VC++ 10.0 (VS2010) Memory: 4k page, physical 2074576k(402044k free), swap 4149152k(1441572k free) CommandLine flags: -XX:InitialHeapSize=16777216 -XX:MaxHeapSize=268435456 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:-UseLargePagesIndividualAllocation Heap def new generation total 4928K, used 885K [0x04600000, 0x04b50000, 0x09b50000) eden space 4416K, 20% used [0x04600000, 0x046dd5f0, 0x04a50000) from space 512K, 0% used [0x04a50000, 0x04a50000, 0x04ad0000) to space 512K, 0% used [0x04ad0000, 0x04ad0000, 0x04b50000) tenured generation total 10944K, used 0K [0x09b50000, 0x0a600000, 0x14600000) the space 10944K, 0% used [0x09b50000, 0x09b50000, 0x09b50200, 0x0a600000) Metaspace used 98K, capacity 2242K, committed 2368K, reserved 4480K
編寫了一個簡單的遞迴計算Fibbonaccy數列的程式,並在最後增加了一句System.gc()強制進行Full GC,得到的GC日誌如下,回收後Eden區使用量為0。當伺服器型的應用執行起來時,免不了會有多次GC,通過GC日誌可以比較容易定位效能問題,比如 full gc次數過多等。
{Heap before GC invocations=0 (full 0): def new generation total 4928K, used 1522K [0x04600000, 0x04b50000, 0x09b50000) eden space 4416K, 34% used [0x04600000, 0x0477c9e8, 0x04a50000) from space 512K, 0% used [0x04a50000, 0x04a50000, 0x04ad0000) to space 512K, 0% used [0x04ad0000, 0x04ad0000, 0x04b50000) tenured generation total 10944K, used 0K [0x09b50000, 0x0a600000, 0x14600000) the space 10944K, 0% used [0x09b50000, 0x09b50000, 0x09b50200, 0x0a600000) Metaspace used 259K, capacity 2280K, committed 2368K, reserved 4480K 15.463: [Full GC (System.gc()) 15.477: [Tenured: 0K->776K(10944K), 0.0428098 secs] 1522K->776K(15872K), [Metaspace: 259K->259K(4480K)], 0.0688257 secs] [Times: user=0.02 sys=0.00, real=0.07 secs] Heap after GC invocations=1 (full 1): def new generation total 4992K, used 0K [0x04600000, 0x04b60000, 0x09b50000) eden space 4480K, 0% used [0x04600000, 0x04600000, 0x04a60000) from space 512K, 0% used [0x04a60000, 0x04a60000, 0x04ae0000) to space 512K, 0% used [0x04ae0000, 0x04ae0000, 0x04b60000) tenured generation total 10944K, used 776K [0x09b50000, 0x0a600000, 0x14600000) the space 10944K, 7% used [0x09b50000, 0x09c122f0, 0x09c12400, 0x0a600000) Metaspace used 259K, capacity 2280K, committed 2368K, reserved 4480K } Heap def new generation total 4992K, used 45K [0x04600000, 0x04b60000, 0x09b50000) eden space 4480K, 1% used [0x04600000, 0x0460b4a8, 0x04a60000) from space 512K, 0% used [0x04a60000, 0x04a60000, 0x04ae0000) to space 512K, 0% used [0x04ae0000, 0x04ae0000, 0x04b60000) tenured generation total 10944K, used 776K [0x09b50000, 0x0a600000, 0x14600000) the space 10944K, 7% used [0x09b50000, 0x09c122f0, 0x09c12400, 0x0a600000) Metaspace used 259K, capacity 2280K, committed 2368K, reserved 4480K
測試環境:
JVM: Java HotSpot(TM) Client VM (25.60-b23, mixed mode, sharing)Java: version 1.8.0_60, vendor Oracle Corporation
Step1:
監控介面,能夠直觀地看到JVM各個區域的記憶體使用情況。除了監視虛擬機器,還可以監視應用的記憶體使用情況。嘗試對Eclipse的啟動進行優化,可以很明顯的看到eclipse啟動時,S0的物件向S1拷貝,而old區的使用量略微增加。
還可以監控當前CPU和記憶體負載情況,類載入數量和執行緒數:
Step2:
eclipse安裝目錄下有個.ini檔案,即eclipse的配置檔案,將需要的引數配置填寫進去即可。啟動eclipse後,檢視GC日誌,發現整個啟動過程中共發生了27次GC,其中有3次Full GC,整個啟動過程,我掐指一算大概6~7秒,相當慢了。
通過引數-XX:NewSize=n 將Eden區重設為128m後,再次啟動eclipse發現,這次就只發生了11次GC,下降了一般還多,啟動速度也明顯比原來快了一些。
Step3:
接著使用-Xverify:none禁掉類載入時的位元組碼驗證過程 ,GC次數為10次, 現在啟動速度基本為3秒多左右,比原來快了一倍。
{Heap before GC invocations=10 (full 4): def new generation total 36864K, used 35041K [0x04400000, 0x06bf0000, 0x0eea0000) eden space 32832K, 100% used [0x04400000, 0x06410000, 0x06410000) from space 4032K, 54% used [0x06410000, 0x06638570, 0x06800000) to space 4032K, 0% used [0x06800000, 0x06800000, 0x06bf0000) tenured generation total 57224K, used 40267K [0x0eea0000, 0x12682000, 0x24400000) the space 57224K, 70% used [0x0eea0000, 0x115f2ca0, 0x115f2e00, 0x12682000) Metaspace used 31836K, capacity 33329K, committed 33408K, reserved 34176K 5.742: [GC (Allocation Failure) 5.742: [DefNew: 35041K->4031K(36864K), 0.0193873 secs] 75308K->46613K(94088K), 0.0194649 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
然而這種調優粒度太粗,伺服器上程式需要定位更精確的時間,所以需要更多引數來除錯。
相關文章
- 記一次jvm調優及垃圾收集器JVM
- 一次快速排序引發的jvm調優排序JVM
- JVM引數調優八大技巧之筆記JVM筆記
- JVM調優JVM
- JVM調優總結-調優方法JVM
- JVM效能調優,記憶體分析工具JVM記憶體
- JVM調優策略JVM
- "簡單"的jvm調優JVM
- “簡單”的jvm調優JVM
- RabbitMq 最全的效能調優筆記MQ筆記
- JVM調優總結(十)-調優方法JVM
- JVM調優筆記(一)--Nacos GC引發的服務批次下線問題JVM筆記GC
- 記一次SQL調優過程SQL
- 效能調優(cpu/IO/JVM記憶體分析)JVM記憶體
- 記一次我的 MySQL 調優經歷MySql
- python效能調優的一次記錄Python
- JVM調優推薦JVM
- JVM調優淺談JVM
- JVM原理講解和調優,記憶體管理和垃圾回收,記憶體調優JVM記憶體
- JVM調優工具Arthas的使用JVM
- jvm系列(七):jvm調優-工具篇JVM
- JVM調優:HotSpot JVM垃圾收集器JVMHotSpot
- ☕【JVM技術指南】「JVM總結筆記」Java虛擬機器垃圾回收認知和調優的"思南(司南)"【下部】JVM筆記Java虛擬機
- JVM 調優命令&工具使用JVM
- JVM 引數調優(qbit)JVM
- JVM調優-學習篇JVM
- JVM 調優(學習篇)JVM
- JVM常用調優引數JVM
- Java jvm 診斷調優JavaJVM
- JVM 調優示例和配置JVM
- 筆記:JVM筆記JVM
- Laravel 高併發調優筆記Laravel筆記
- 記錄一個cpu彪高的BUG處理--jvm調優JVM
- 【JVM】堆體系結構及其記憶體調優JVM記憶體
- JVM記憶體引數詳解及其配置調優JVM記憶體
- 記一次SQL Server刪除SQL調優SQLServer
- 一次徹底掌握資料中心級的JVM調優實戰經驗JVM
- 【JVM進階之路】十:JVM調優總結JVM