Jan 22, 2012

[Java Performance]Chapter 3:JVM Overview

這篇談JVM的概觀與各元件用途與描述HotSpot VM在各元件上的優化技術

HotSpot VM High Level Architecture

JVM 有三個主要元件:
  • VM Runtime
  • JIT Compiler
  • Memory manager





32bit JVM限制使用4 GB的記憶體,在windows約能配置1.5G左右的Java Heap Space,Linux 約 2 .5G~3G的 Java Heap Space,新版的linux kernel約能配置在 2G左右,solaris在3.3G,64bit JVM則能使用更多的記憶體。

Java objects在Hotspot內部的 reference 稱為 ordinary object pointer(oops),64bit JVM的oops與32bit JVM相較會佔用更多CPU cache,少8~15 %的CPU cache效率,與佔用更多Java Heap Space;在HotSpot VM可經由壓縮指標的方法將完整的64bit壓縮成32bit空間,其啟動方式在JVM 加入選項為 -XX:+UseCompressedOops (該功能在新版JDK版本已預設啟用)。

HotSpot VM Runtime

Hotspot VM Runtime的職責
  • parsing of command line arguments
  • VM life cycle
  • class loading
  • byte code interpreter
  • exception handling
  • synchronization
  • thread management
  • Java Native Interface 
  • VM fatal error handling
  • C++ heap management 
Command Line Options
共分成三類
  • standard options 規範於JVM Spec,通用於各種發行版本(e.g HotSpot、JRocket、J9)
  • nonstandard options 出現在不同發行版本不同版次的JVM,使用-X做為prefix
  • developer options 出現在不同發行版本不同版次的JVM,使用-XX做為prefix,用+ 或 - 表示啟用或停用功能 e.g -XX:+AggressiveOpts 表示啟用, -XX:-AggressiveOpts 表示停用,部份option能有額外參數如 -XX:OptionName= ,表示可填入數字,可用k/m/g做為suffix,當作kilo/mega/giga單位
VM lifecycle
Hotspot VM Runtime負責launching和shutdown Hotspot VM,使用java, javaw(without console), javaws(java web start)
  1. 解析command line options
  2. 設立Java Heap sizes與JIT compiler 類型
  3. 設立Environment variables像是LD_LIBRARY_PATH與CLASSPATH
  4. 如果command line沒有定義Main-Class,則從JAR中的manifest取得Main-Class
  5. 使用JNI_CreatJavaVM創建Hotspot VM
  6. 當Hotspot VM完成初始化,Main-Class 載入與取得 main method 參數
  7. 執行 main method
當 main method 執行完成會檢查有無 exception、呼叫JNI DetachCurrentThread 操作減少thread count、檢查thread count數量確保已沒有進行中的操作、shutdown Hotspot VM(JNI_DestoryJavaVM)。

VM Class Loading
class loading 指的是mapping class name 或 interface name 到 class object 的處理過程,常見的class loading 發生在bytecode resolution,在Java API部份則透過Class.forName()、ClassLoader.loadClass()、reflection API、JNI_FindClass觸發。

java.lang.Object、java.lang.String、java.lang.System、java. lang.Thread、java.lang.ThreadGroup、java.lang.reflect.Method、java.lang.ref.Finalizer, java.lang.Class 等在 JNI_CreatJavaVM 時即已載入。

Class Loading Phases
1.load class phase
使用 class/interface name 去尋找binary的Java classfile,沒找到拋出NoClassDefFound error,此外這階段會檢查classfile syntax,驗證不過拋出ClassFormatError 或 UnsupportedClassVersionError(e.g 編譯jdk 7版本在jdk 6執行),完成載入Java class前必需先載入class本身的super class/interface 因此可能會拋出ClassCircularityError、IncompatibleClassChangeError

2.link phase
檢查classfile semantics、constant pool symbols、type checking 驗證不過拋出VerifyError,這階段會做semantic check用於解析相關的symbolic reference

3.initializing phase
初始化static fields、static initializer(第一次執行Java Code),若有superclass則會先initializing superclass

Class Loader Delegation
class loader 可以經由其它class loader 代理載入class,在Java SE class loader 的階層,搜尋會從下層住上層(parent)尋找class
  • bootstrap class loader 從Hotspot VM的BOOTCLASSPATH載入 class, 如 rt.jar 等Java SE的libraries 
  • extension class loader 從$JRE_HOME/lib/ext載入class
  • system class loader 從 CLASSPATH 載入 class,也是預設的class loader 
  • application class loader 則由開發著自訂class載入路徑,如web container 的實作會為每個webapp分配獨立的 class loader 用於載入/WEB-INF/classes、/WEB-INF/libs/*中的class
Type Safety
Java class / interface 完整的名稱包含了package 名稱與 Class name ,於class loader 本身也定義了一層 namespace,所以有種狀況是A class loader 與 B class loader 載入同樣名稱的class但會被區分成不同的class,因此若有相互引用的狀況下Hotspot會做相容檢查(e.g 同method signature 則相容,可使用),失敗則丟出LinkageError

Interpreter
HotSpot VM interpreter 是基於 template 方法的 interpreter,JVM startup 時 HotSpot VM runtime 會產生與放置於記憶體中,其內部資訊的資料結構稱為 TemplateTable,用於對應每個byte code的machine dependent code的參考關系,於debug版本的Hotspot VM 可以用-XX:PrintInterpreter看TemplateTable。

Synchronization
HotSpot VM對同步化提供最佳化機制,並區分為無競爭與競爭

  • 無競爭 可以啟用偏向鎖(-XX:+UseBiasedLocking)(JDK 1.6預設開啟),當有同步化的需要時再由輕量級鎖(CAS)轉成重量級鎖
  • 競爭  啟用adaptive spinning(JDK 1.6預設開啟) 透過一段時間在同Thread執行週期裡重試取得 lock,避免thread離開/進入檢查lock是否可用,降低context switch


Thread Management
Thread Model
Hotspot VM的Java Thread(VM 底層) / java.lang.Thread / OS thread 是一對一對應的,Java Thread start時候作業系統創建 OS Thread,Java Thread terminated 時作業系統回收OS Thread;thread 的scheduling 由作業系統分配到可用的CPU上,於thread priorities則各作業系統不一樣。

Internal VM Threads

  • VM Thread 負責處理VM operation
  • Period task thread 也稱WatcherThread用於模擬timer interrups
  • Garbage collection threads 用於GC
  • JIT compiler threads 用於 runtime 時編譯 bytecode 為 machine code
  • Signal dispatcher thread 用接收process signal 與分派給Java level的signal handling method (e.g kill  pid時丟給 Shutdown Hooks API)


VM Operations and Safepoints
VM Thread會從VMOperationQueue取得操作指令,通常這類的操作指令是為了讓Hotspot VM進入safepoint,進入safetpoint時所有Java executing thread會block,native code 會預防執行結果傳回 Java code,因此在safepoint進行中不會有中途變更Java Heap,最常件的VMOperation就是支援garbage collection時的先置操作,也是俗稱的 stop-the-world

其它像是偏向鎖的revocation(切換成重量級鎖)、thread stack dumps、 thread suspension 與 stopping (i.e java.lang.Thread.stop())、JVMTI的修改操作 都會需要進入 safepoints

VM Fatal Error Handling
OS拋出 segmentation fault(linux) / Access Violation error(windows) 時 HotSpot VM預設會在Launcher起動的地方產生記錄檔 hs_err_pid.log,或者以 -XX:ErrorFile 指定紀錄檔存放路徑,也能以 -XX:OnError=cmd1 args; cmd2 指定VM crash時的執行指令,OnError參數需作業統系統的debugger tools支援(e.g GDB),有些發行版本不支援OnError功能或著需要偵查附加debugger前的錯誤,可以改用-XX:+ShowMessageBoxOnError 選項。

JDK 1.6對於 OutOfMemoryError 使用特定選項

  • -XX:OnOutOfMemoryError=cmd1 args; cmd2
  • -XX:+HeapDumpOnOutOfMemoryError
  • -XX:HeapDumpPath=

HotSpot VM Garbage Collectors
Generational Garbage Collection
HotSpot VM基於以下兩個假設,使用年代做為垃圾收集的方法
  • 大部份物件很快就不在使用
  • 從舊物件到新物件間的關聯較少
The young generation
新產生的物件配置於這裡,這裡配置的空間較小,GC耗費時間相對短暫, GC執行次數較頻繁,基於大部份物件很快就不使用的假設,young GC(minor garbage collection)能有效率的大量清除垃圾物件

The old generation
物件會長時間使用,最終會從young generation 提升到 old generation ,在這個年代配置的空間通常比young generation大,使用空間成長較為緩慢,執行GC(major garbage collection)的次數較少,但耗費時間較長

The permanent generation
這年代是由HotSpot VM 存放metadata,如class data structure, interned string


分析是否為不可用的物件需要做object graph的分析,由於會有old-to-young的關聯存在因此也需要掃描old generation物件,Hotspot VM 為了降低minor GC要掃描的old generation範圍,使用一種資料結構Card Table對應整個old generation空間,若old generation物件有變更reference field 那也會在Card Table標記該物件所在區塊為修改過(dirty),當minor GC時old generation只要掃描修改過區塊的物件,不用掃描所有在old generation的物件。
The Young Generation
young generation 又細分成三塊區域,eden用於配置新物件,兩塊 Survivor space 給物件累積存活次數(minor gc掃描),當物件存活次數超過設定值(TenuringThreshold),再將物件提升到Old generation,其目地是要讓短期物件在young generation回收,老物件提升到Old generation。

由於Survivor space空間可能會不足以接收來至於edem的物件,這種狀況下可能會將短期的物件直接提升到Old Generation,也稱為premature promotion,因此造成Old Generation空間快速的的被短期物件填滿,造成更多major GC;當minor gc從eden要提升物件,但Survivor space與Old Generation都放不下的時候會造成major GC,也稱為 promotion failure(gc log可看到) 。

Fast Allocation
為了要快速配置物件到Young Generation,GC會在執行完成後在將該區塊給清空(Copying Garbage Collector),當要配置物件時可以連續配置在物件尾端,此方法稱為bump-the-pointer。

在multithread application時為了避免同時配置eden會需要全域鎖,因此每個Thread都有配置一塊空間放置新物件,稱為Thread-Local Allocation Buffers(TLABs)每個thread都擁有一部份獨立的eden區塊,不會有其它Thread共用因此不需要鎖,當使用的buffer滿載的時候再同步化使用 eden。

Garbage Collectors:Spoiled for Choice
HotSpot VM有四種garbage collector每種garbage collector有不用的使用策略

The Serial GC
Serial GC在young generation的方法就前面所描述的GC過程,在Old generation則使用 mark-compact garbage collector 將物件標記清除,再將保留下的物件移動與放一起,然而Serial GC限制於僅能使用單個Virtual Processor,若硬體環境下有多個Virtual Processor就會閒滯多餘的Virtual Processor效能,因此Serial GC適用pause time要求不高的應用程式與執行多VM的環境(e.g 同時運行多個GUI應用程式)


The Parallel GC:Throughput Matters!
Parallel GC運作方式類似Serial GC但同時可以使用多個Virtual Processor 進行GC,相對Serial GC可以增加整體GC效率與Throughput,適用於batch processing、scientific computing 等要求Throughput、不要求latency的應用程式

The Mostly-Concurrent GC:Latency Maters!
由於Serial GC與Parallel GC在 GC時都會需要stop-the-world,而major GC時掃描的物件較多,停滯時間較長,並不適用於要求反應速度的應用,Concurrent Mark-Sweep GC是種適用於major GC的collector能有較短暫停滯,也能運用多個CPU,所以較適用於有要求低延遲的應用,其GC過程有分4個階段,在initial mark與remark階段才需要stop-the-world,但代價是整體GC時間較長、空間效率較差、throughput 較低。
The Garbage-Firs t GC:CMS Replacement
在JDK 1.6u20後期引入,未來用於替代CMS GC,其方法將Java Heap分成許多相等大小的區塊稱為Regions,並由G1動態調整Regions做為Young Generation空間,大部份的GC時間僅用在Young Regins(Young Generation),具備CMS優點,且較省空間

Comparisons
以下簡要的說明Application如何影響GC的工作
  • Allocation:當各Generation達到特定的可用空間限制即會啟動GC(CMS需自定比率,其它則等到各Generation滿載),當application的allocation rate越高則GC更頻繁
  • Live data size:當物件越多,GC要做更多的分析
  • Reference updates in the old generation:當Old Generation的物件變更Reference頻繁則minor GC檢查old-to-young與CMS remark的要分析的變更區塊就越多




Hotspot VM JIT Compilers
javac 產出bytecode,JIT會將bytecode轉為intermediate representation(IR)並做優化如:constant foldingcommon subexpression eliminationinlining of function較複雜優化技術有improving the execution of loop range check eliminationunrollingloop invariant code motion,接著依不同的編譯策略將IR轉換成machine code(instruction selection與分配值給暫存器)。

Hotspot的JIT Compiler分為兩種
  • Client JIT Compiler的目標在快速啟動與快速編譯因此適用於反應快速的GUI application,採用較簡化的優化方法但編譯時間較短,整體的效能會較Server JIT Compiler差

  • Server JIT Compiler的目標在高效能與高吞吐的Java application,會最大化的運用編譯優化策略,編譯優化的內容較多也較為複雜,編譯的時間較Client JIT Compiler長


JAVA 6 HostSpot VM提供混合式的JIT Compiler,稱為tiered compilation,提供Client JIT Compiler快速啟動的能力與Server JIT Compiler的效能優化,使用 -server -XX: +TieredCompilation 啟用,適用於JAVA 6 u25之後。

HotSpot VM Adaptive Tuning
Java 5 HotSpot VM 會在JVM啟動時依硬體規格與作業系統環境自動選擇 garbage collector、設定Java Heap size、 JIT Compiler,其自動調整Java Heap的方式稱為ergonomics

Java 5 HotSpot VM會依2G記憶體,2 virtual processors識別為server-class machine,並對server-class machine採用Parallel GC、server JIT compiler、1/64~1GB做為最小Java Heap Size、1/4~1GB 做為最大Java Heap Size,可是特定的作業系統(windows 32bit)與隨著JDK持續演進此標準不太一定,可透過 java -XX:+PrintCommandLineFlags -version得知實際環境的ergonomics預設值。

Adaptive Java Heap Sizing
ergonomics對Parallel GC(UseParallelGC/UseParallelOldGC)額外啟動adaptive heap sizing功能,透過評估 object allocation rate 與 object lifetime 自動決定 young generation 空間大小,若有指定-Xmn,-XX:NewSize,-XX:MaxNewSize,-XX:NewRatio,-XX:SurvivorRatio 則會做為初始值並再自動調整,若要自行調整可經由-XX:-UseAdaptiveSizePolicy關閉adaptive heap sizing。


補充

不同作業系統不同版本在預設的Command Options與Ergonomics機置都有異因此在Java HotSpot VM 23後的版本可透過 -XX:+PrintFlagsFinal 得知相關的執行參數結果

e.g.java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version


Reference:
Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html

Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

Memory Management in the Java HotSpot™ Virtual Machine
http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

Troubleshooting and Diagnostic Guide
http://java.sun.com/j2se/1.5/pdf/jdk50_ts_guide.pdf

John Coomes, Peter Kessler, Tony Printezis Garbage Collection-Friendly Programming
http://dl.dropbox.com/u/2331008/docs/TS-2906.pdf

No comments: