1. 程式人生 > >JAVA虛擬機器—JVM

JAVA虛擬機器—JVM

        前言:JVM虛擬機器是java程式執行平臺,對於一個java程式是十分重要的。在執行時JVM虛擬機器的類載入器將.class檔案載入到虛擬機器中轉化為虛擬機器的可執行檔案執行,在現在的虛擬機器中提供了一個HotSpot(熱點探測)機制,頻繁讀取達到某個閾值的.class檔案,會直接被以虛擬機器可執行的檔案存放在虛擬機器中。JVM虛擬機器中有兩個可選機制版本client和server,clident機制為預設機制,如想更改JVM機制版本在jdk\jre\lib\i386\jvm.cfg檔案中更改,將server KNOWN放在client KNOWN上即可。client機制一般運行於客戶端應用,消耗較少的計算機資源;server機制一般用於服務端應用,消耗較多的計算機資源。

一:JVM的結構

1:JVM結構組成

(1)類載入子系統與方法區:類記載子系統負責從檔案系統或網路中載入class資訊,載入的類資訊放在方法區中,方法區還會存放執行時的常量池資訊,包括字串和數字常量

(2)JAVA堆:java堆在虛擬機器啟動時建立,他是java程式最主要的記憶體工作區域,幾乎所有的物件例項都存放在堆中。堆是所有執行緒共享的

(3)直接記憶體:java的NIO庫允許java程式直接使用記憶體。直接記憶體是JAVA堆外的。直接向系統申請的記憶體空間。訪問直接記憶體的速度一般會高於java堆。直接記憶體的大小不受JVM的限制,只要不高於實體記憶體就行。

(4)垃圾回收系統:垃圾回收系統是java虛擬機器的重要組成部分,垃圾回收系統可以對方法區,java堆和直接記憶體進行回收。其中,java堆是垃圾收集器的工作重點。java的垃圾回收系統不同於C++,自動在後臺工作,不需要手動控制。

(5)JAVA棧:每個java虛擬機器執行緒都有一個私有的棧,一個執行緒的java棧線上程被建立時建立,java棧中儲存著區域性變數,方法引數,同時和java方法的呼叫,返回密切相關

(6)本地方法棧:本地方法棧用於本地方法的呼叫,java虛擬機器允許java程式呼叫系統本地系統的本地方法。其他的和java棧類似。

(7)PC暫存器:每個執行緒的私有空間。在任何時刻,一個執行緒總在執行一個方法。這個正在被執行的方法稱為當前方法。如果當前方法不是本地方法,PC暫存器就會指向當前正在被執行的指令。如果當前方法是本地方法,那麼PC暫存器指向undefined

(8)執行引擎:執行引擎是JVM最核心的元件之一,它負責執行虛擬機器的位元組碼,現代虛擬機器為了提高執行效率,會使用即時編譯技術將方法編譯為機器碼後執行。

2:JAVA堆的分代

(1)為什麼要分代

       堆記憶體是java虛擬機器管理記憶體中最大的一塊,也是垃圾回收系統回收最頻繁的一段。為了方便垃圾回收和記憶體管理,引入了堆的分代,堆的分代以物件的生存時間為標準,將堆分為了新生代(年輕代),老年代和永久代。這樣在一定程度上提高了垃圾回收的效率,而且儘可能減少了記憶體碎片的產生

(2)記憶體分代劃分

       java虛擬機器將堆記憶體分為新生代(年輕代),老年代,永久代,永久代是HotSpot的特有概念,它採用永久代的方式實現方法區。而HotSpot也有去永久化的趨勢,jdk1.7已經將字串常量池從永久代移除。永久代主要存放常量,類資訊,靜態變數等資料,與垃圾回收關係不大。新生代和老年代是垃圾回收的主要區域,垃圾回收時以新生代和老年代之間為分界線使用不同的垃圾回收器,記憶體分代示意圖如下:

2-1:新生代(年輕代)

       新生成的物件優先存放於新生代中,新生物件朝生夕死,存活率很低。常規進行一次垃圾回收,新生代會回收70%-95%的空間,回收效率很高

       HotSpot將新生代劃分為三塊,一塊較大的Eden區和兩塊較小的Survivor區(from區和to區),from有時被稱為S0區,to有時被稱為S1區,預設的比例為8:1:1。劃分的目的是HotSpot以複製演算法回收新生代。新建立的物件會儲存在Eent中,大物件直接進入老年代。當Eent沒有足夠的空間進行分配時,虛擬機發起一次Minor GC,GC開始時候,to區域中是空的(每次都是),GC開始時,Eent區域中存活下來的物件會被複制到to中,from中物件的年齡值達到年齡閾值的(通常是15,物件每逃過一次GC,年齡值+1,GC分代年齡存放在物件的Header中),被複制到老年代中,沒有達到閾值的被複制到to區中,然後清空Eden區和to區,from區和to區交換身份。現在to區便又為空,為下次GC提供空間。

2-2:老年代

       在新生代中經歷了多次GC後存活下來的物件會儲存到老年代。老年代中的物件的生命週期較長,存活率比較高,在老年代中進行GC的頻率相對較低,而且回收的速度也比較慢。

2-3:永久代

       永久代存放類資訊,常量,靜態變數,即時編譯器編譯後的程式碼等資料沒,對這一區域而言,一般而言不進行垃圾回收

二:JVM垃圾回收演算法和收集器

1:垃圾回收常見演算法

(1):引用計數

       比較古老的回收演算法,原理是此物件有一個引用,增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,回收計數為零的物件,此演算法的缺點是無法處理迴圈引用問題

(2):複製

       此演算法把記憶體空間劃分為兩個相等的區域,每次只使用其中一個區域。垃圾回收時將正在使用的物件複製到另一個區域。此演算法只處理正在使用的物件,因此複製成本低,而且複製時可以進行記憶體整理,不會出現記憶體碎片。缺點是需要兩倍的記憶體空間

(3):標記-清除

      此演算法執行分兩階段,第一階段從引用根節點開始標記所有被引用的物件,第二次遍歷整個堆,把未標記物件的清除,此演算法需要暫停整個應用,而且會產生記憶體碎片

(4):標記-整理    

     此演算法執行分兩階段,第一階段從引用根節點開始標記所有被引用的物件,第二次遍歷整個堆,把未標記物件的清除,並吧存活物件壓縮到堆的某一部分,此演算法不會產生碎片,也不會浪費空間,結合了2,3的優點。

2:JVM中垃圾收集器

(1):Scavenge GC(次收集)和Full GC(全收集)的區別

       新生代 GC(Scavenge GC || Minor GC):作用於新生代的GC,執行十分頻繁,回收速度也比較快,當Eden空間不足以為物件分配空間時,會觸發Scavenge GC。一般當Eent區為物件分配空間失敗時,會觸發Scavenge GC,對Eent區進行GC,清除非存活物件,將存活物件儲存到Survivor區。然後整理Survivor兩個區。Eent區的GC非常頻繁,所以採用速度快,效率高的演算法。

       老年代 GC(Full GC || Major GC):發生在老年代的GC,GC時至少伴隨一次Minor GC,Full GC比Minor GC慢十倍以上。當老年代記憶體不足,或呼叫System.gc()方法時,會觸發Full GC。

次收集:

當新生帶對堆空間緊張時會被觸發,收集間隔較短

全收集:

老年堆或持久代空間滿時會被觸發,也可以使用System.gc()方法來啟動

3:分代垃圾收集器

3-1:新生代(年輕代)收集器

(1)序列收集器(Serial)

       Serial收集器是HotSpot執行在Client模式下的預設新生代收集器。使用複製演算法,他的特點是隻用以一個CPU/一條執行緒去完成工作,且在GC時必須要暫停其他所有的工作執行緒(STW,可以使用-XX:+UseSerialGC開啟)。這一點通過安全點的觸發和釋放完成

(2)並行收集器(ParNew)

      ParNew是前面Serial的多執行緒版本,除使用多條執行緒進行GC外,其他的控制引數,收集演算法,物件分配規則,回收策略等和Serial完全相同(也是使用VM啟用老年代CMS收集器-XX:useConMarkSweepGC的預設新生代收集器,useConMarkSweepGC收集器也可以與Serial收集器搭配使用),在單核處理器下的效率不一定高於單執行緒的Serial,而且多執行緒切換麻煩,所以可以通過XX:ParallelGCThreads=<N>引數控制參與GC的執行緒數量。減少了安全點的時間,即減少了使用者執行緒等待的時間,從理論上垃圾收集執行緒可以與工作執行緒一同執行,但實際上並未完全實現這一點

(3)Parallel Scavenge收集器

      與ParNew類似,Parallel Scavenge也是使用複製演算法,也是並行多執行緒收集器,但是與其他收集器儘可能縮短垃圾收集時間不同,Parallet Scavenge收集器更關注系統吞吐量(系統吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間))。停頓時間越短越適用於使用者互動的程式,良好的響應速度可以提高使用者體驗,而高吞吐量則適用於後臺運算而不需要太多互動的任務,最高效率利用CPU時間。

Parallel Scavenge提供瞭如下引數設定系統吞吐量

Parallel Scavenge收集器引數 描述
-XX:MaxGCPauseMillis (毫秒數)收集器將盡量保證記憶體回收時間不超過設定值,但如果太小會導致GC的頻率增加
-XX:GCTimeRatio (整數:0<GCTimeRatio<100)是垃圾收集時間點

 

 

 

 

 

3-2:老年代收集器

(1)Serial Old收集器

      Serial Old是Serial收集器的老年代版本,同樣是單執行緒收集器,使用“標記-整理”演算法(老年代沒有Survivor區,無法使用複製演算法)。可以與任何一個新生代收集器配合使用。

(2)Parallel Old 收集器

       Parallel Old是Parallel Scavenge的老年代版本,使用多執行緒和“標記-整理”演算法,吞吐量優先,主要與Parallel Scavenge配合使用。

(3)CMS收集器

       CMS(Concurrent Mark Sweep)收集器是一款劃時代的收集器,一枚真正意義上的併發收集器(不用暫停使用者執行緒,使用者執行緒和GC執行緒並行,稱為併發)。雖然現在有理論上表現更好的G1收集器,但主流的網際網路企業線上選用的仍然是CMS。CMS是一種以獲取最短回收停頓時間為目標的收集器,CMS又稱為多併發低暫停的收集器,基於“標記-清除”演算法(CMS可以與Serial,ParNew新生代收集器配合使用,預設使用ParNew)大概分為以下四個步驟:1),3)需要暫停工作執行緒

1):初始標記(initlal mark)

2):併發標記(CMS Concurrent Mark:GC rootsTracing過程)

3):重新標記(CMS remark)

4):併發清除(CMS concurrent Sweep,死亡物件被就地釋放。注意:此處沒有壓縮)

3-3:分割槽收集器-G1收集器

       G1收集器是面向伺服器端的收集器,主要目的是配備多核CPU的伺服器治理大記憶體,G1收集器保留老年代,新生代,永久代的概念,但是不基於分代處理,而是將堆分為多個大小相等的獨立區域。可用於整個堆的垃圾回收。
--xx:+UseG1GC  啟用G1收集器

三:JVM的調優

1:JVM自帶的監測小工具

(1)jps

用於檢視JVM中所有程序的具體狀態,包括程序的PID,程式路徑等

jps -v   //輸出JVM引數

jps -q   //輸出JVM中各個執行緒的PID

jps -m   //輸出main方法的引數

jps -l    //輸出完全的包名,主類名,PID等

(2)jstat 

監測資源和效能,監測JVM記憶體中各個區域的記憶體大小和使用量

結構:jstat [option]  [pid]  [time>]  [count]

option  //可選引數

time  //間隔多長時間列印一次,單位毫秒

count  //列印多少次,不寫無限列印

option:

jstat -compiler [pid]   //顯示JVM實時編譯的數量等資訊

jstat -class [pid]   //顯示載入類的數量,和所佔用的記憶體空間

jstat -gc [pid]   //顯示GC的資訊,檢視各區的分配大小和使用量

jstat -gcutil [pid]   //統計GC資訊

jstar  -gccapacity [pid]  //JVM中三代物件的記憶體使用和佔用狀態

jstat -printcompilation [pid]  //當前JVM執行的資訊

(3)jmap

可以獲得JVM的堆中的詳細資訊,從而分析堆的執行狀態

結構:

jmap [option] [pid]

引數:

jmap -heap [pid]   //列印堆的資訊。GC演算法,記憶體大小和使用情況等

jmap -histo [pid]   //列印堆的物件資訊,包括記憶體大小等

jmap -histo:live  [pid]   //會先觸發GC

jmap -finalizerinfo   //列印等待回收的物件資訊

(4)jstack

列印棧的詳細資訊,從而分析棧的執行狀態

結構:jstack [option] [pid]

引數

jstack -F [pid]   //當jstack -l [pid]沒有響應時強制列印棧資訊

jstack -l [pid]   //長列表,列印鎖的附加資訊

jstack -h  //列印幫助資訊

(5)jconsole

監控java應用程式

jconsole [pid]  //本地監控

jconsole [IP:port]   //遠端監控   

遠端監控需要配置jmx代理資訊,如修改tomcat的bin目錄下吃的catalina.bat檔案

set JAVA_OPTS= %JAVA_OPTS% -Djava.rmi.server.hostname=HostIP
set JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.port=8888
set JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.ssl=false
set JAVA_OPTS= %JAVA_OPTS% -Dcom.sun.management.jmxremote.authenticate=false

(6)jvisualvm

jdk自帶的圖形話監測工具,可以使用外掛,如Visual GC(專門用於監測垃圾回收系統的外掛)

2:JVM引數介紹

引數的使用環境(多個引數用空格隔開):

(1)可以在eclipse中設定引數

 run as->run configurations->選擇執行的程式->arguments->VM arguments->run

(2)命令列中設定引數

(3)基於JVM的程式自帶引數設定,如Tomcat

 Tomcat預設可以使用的記憶體為128MB。

        Windows下,在檔案/bin/catalina.bat中,在rem Guess CATALINA_HOME if not definedset CURRENT_DIR=%cd%後面新增JVM引數

        Unix下,在檔案/bin/catalina.sh中,在檔案頭部設定JVM引數

配置大概如下:

JAVA_OPTS="-Xms50m -Xmx100m -XX:ParallelGCThreads=8"

2:一些引數

-Xms //初始堆大小,如-Xms10m

-Xmx  //最大堆大小,如-Xmx100m

-XX:NewSize=n  //設定新生代大小

-XX:MaxPermSize=n  //設定持久代大小

-XX:NewRatio=n   //年輕代和老年代的比值

-XX:SurvivorRatio=n   //Eden區和Survivor區的比值

XX:+UseSerialGC   //設定序列收集器

-XX:+UseParallelGC  //設定並行收集器

-XX:+UseParalledlOldGC   //設定並行年老代收集器

-XX:+UseConcMarkSweepGC   //設定併發收集器

等等等等。。。。。

官方英文文件:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

非官網中文文件:http://www.jvmer.com/jvm-xx-%E5%8F%82%E6%95%B0%E4%BB%8B%E7%BB%8D/