JVM史上最全實踐優化沒有之一

1.jvm的運行參數

在jvm中有不少的參數能夠進行設置,這樣可讓jvm在各類環境中都可以高效的運行。絕大部分的參數保持默認便可。html

1.1 三種參數類型

jvm的參數類型分爲三類,分別是 :
	標準參數 :
		-help
		-version
	-X參數(非標準參數)
		-Xint
		-Xcomp
	-XX參數(使用率較高)
		-XX:newSize
		-XX:+UseSerialGC

1.1.1 -server與-clinet參數

能夠經過-server或-client設置jvm的運行參數。
	(1)它們的區別是Server VM的初始堆空間會大一些,默認使用的是並行垃圾回收器,啓動慢運行快。
	(2)Client VM相對來說會保守一些,初始堆空間會小一些,使用串行的垃圾回收器,它的目標是爲了讓JVM的啓動速度更快,但運行速度會比Server VM模式慢些。
	(3)JVM在啓動的時候會根據硬件和操做系統自動選擇使用Server仍是Client類型的JVM。
	(4)32位操做系統
			1)若是是Windows系統,不論硬件配置如何,都默認使用Client類型的JVM。
			2)若是是其餘操做系統上,機器配置有2GB以上的內存同時有2個以上CPU的話默認使用server模式,不然使用client模式。
	(5)64位操做系統
			1)只有server類型,不支持client類型。

2.1 -X參數

jvm的-X參數是非標準參數,在不一樣版本的jvm中,參數可能會有所不一樣,能夠經過java -X查看非標準參數。		
	-Xmixed:混合模式執行(默認)
	-Xint:僅解釋模式執行
	-Xbootclasspath:(用;分隔的目錄和zip/jar文件)設置搜索路徑以引導類和資源
	-Xbootcalsspath/a:(用;分隔的目錄和zip/jar文件)	附加在引導類路徑末尾
	-Xbootcalsspath/p:(用;分隔的目錄和zip/jar文件)置於引導類路徑以前
	-Xdiag :顯示附加診斷消息
	-Xnoclassgc :禁用類垃圾收集
	-Xincgc : 啓用增量垃圾收集
	-Xloggc:<file> : 將GC狀態記錄在文件中(帶時間戳)
	-Xbatch :禁用後臺編譯
	-Xms<size> : 設置初始java堆大小
	-Xmx<size> : 設置最大java堆大小
	-Xss<size> : 設置java線程堆棧大小
	-Xprof : 輸出cpu配置文件數據
	-Xfuture : 啓用最嚴格的檢查,預期未來的默認值
	-Xrs : 減小java/VM 對操做系統信號的使用(請參閱文檔)
	-Xcheck:jni : 對JNI函數執行其餘檢查
	-Xshare:off : 不嘗試使用共享類數據
	-Xshare:auto : 在可能的狀況下使用共享類數據(默認)
	-Xshare:on : 要求使用共享類數據,不然將失敗
	-XshowSettings:all : 顯示全部設置並繼續
	-XshowSettings:vm : 顯示全部與vm相關的設置並繼續
	-XshowSettings:properties : 顯示全部屬性設置並繼續
	-XshowSetting:locale : 顯示全部與區域設置相關的設置並繼續

2.1.1 -Xint、-Xcomp、-Xmixed

在解釋模式(interpreted mode)下,-Xint標記會強制JVM執行全部的字節碼,固然這會下降運行速度,一般低10倍或更多。
	(編譯比較快,運行比較慢)
-Xcomp參數與它(-Xint)正好相反,JVM在第一次使用時會把全部的字節碼編譯成本地代碼,從而帶來最大程度的優化。
	然而,不少應用在使用-Xcomp也會有一些性能損失,固然這比使用-Xint損失的少,緣由是-Xcomp沒有讓JVM啓用JIT編譯器的所有功能。
	JIT編譯器能夠對是否須要編譯作判斷,若是全部代碼都進行編譯的話,對於一些只執行一次的代碼就沒有意義了。
	(
-Xmixed是混合模式,將解釋模式與編譯模式進行混合使用,由jvm本身決定,這是jvm默認的模式,也是推薦使用的模式。

3.1 -XX參數

-xx參數也是非標準參數,主要用於jvm的調優和debug操做。
-xx參數的使用有2種方式,一種是boolean類型,一種是非boolean類型:
	boolean類型
		格式 :-xx:[+-]<name> 表示啓用或禁用<name>屬性
		如 :-xx:+DisableExplicitGC 表示禁用手動調用gc操做,也就是說調用System.gc()無效
	非boolean類型
		格式 :-xx:<name>=<value> 表示<name>屬性的值爲<value>
		如 :-xx:NewRatio=1 表示新生代和老年代的比值

4.1 -Xms與-Xmx參數

-Xms與-Xmx分別是設置jvm的堆內存的初始大小和最大大小。
-Xmx2048m : 等價於-XX:MaxHeapSize,設置JVM最大堆內存爲2048M。
-Xms512m :等價於-XX:InitialHeapSize,設置JVM初始堆內存爲512M。
適當的調整jvm的內存大小,能夠充分利用服務器資源,讓程序跑的更快。

5.1 查看jvm的運行參數

有時候咱們須要查看jvm的運行參數,這個需求可能會存在2中狀況:
第一,運行java命令時打印出運行參數;
第二,查看正在運行的java進程的參數;

5.1.1 運行java命令時打印參數

運行java命令時打印參數,須要添加-XX:+PrintFlagsFinal參數便可。	其中參數有boolean類型和數字類型,值的操做符是=或:=,分別
表明默認值和被修改的值。
查看全部的參數,用法 :jinfo -flags <進程id>
經過jps 或者 jps -l 查看java進程
查看某一參數的值,用法 :jinfo -flag <參數名> <進程id>

6.1 jdk1.7的堆內存模型

Young年輕區(代)
	Young區被劃分爲三部分,Eden區和兩個大小嚴格相同的Survivor區,其中,Survivor區間中,某一時刻只有其中一個是被
	使用的,另一個留作垃圾收集時複製對象用,在Eden區間變滿的時候,GC就會將存活的對象移到空閒的Survivor區間中
	,根據JVM的策略,在通過幾回垃圾收集後,任然存活於Survivor的對象將被移動到Tenured區間。
Tenured年老區
	Tenured區主要保存生命週期長的對象,通常是一些老的對象,當一些對象在Young複製轉移必定的次數之後,	對象就會被
	轉移到Tenured區,通常若是系統中用了application級別的緩存,緩存中的對象每每會被轉移到這一區間。
Perm 永久區
	Perm代主要保存class,method,filed對象,這部分的空間通常不會溢出,除非一次性加載了不少的類,不過在涉及到熱部
	署的應用服務器的時候,有時候會遇到java.lang.OutOfMemoryError : PermGen space的錯誤,形成這個錯誤的很大緣由就
	有多是每次都從新部署,可是從新部署後,類的class沒有被卸載掉,這樣就形成了大量的class對象保存在了perm中,這
	種狀況下,通常從新啓動應用服務器能夠解決問題。
Virtual區
	最大內存和初始內存的差值,就是Virtual區。

6.2 jdk1.8的堆內存模型

由上圖能夠看出,jdk1.8的內存模型是由2部分組成,年輕代 + 年老代。 年輕代:Eden + 2 * Survivor 年老代:OldGen 在jdk1.8中變化最大的Perm區,用Metaspace(元數據空間)進行了替換。 須要特別說明的是 :Metaspace所佔用的內存空間不是在虛擬機內部,而是在本地內存空間中,這也是與1.7的永久代最大的區別所在。 java

6.3 爲何要廢棄1.7中的永久區?

移除永久代是爲融合HotSpot JVM與JRockit VM而作出的努力,由於JRockit沒有永久代,不須要配置永久代。
現實使用中,因爲永久代內存常常不夠用或發生內存泄漏,爆出異常java.lang.OutOfMemoryError : PermGen.
基於此,將永久區廢棄,而改元空間,改成了使用本地內存空間。

6.4 經過jstat命令進行查看堆內存使用狀況

jstat命令能夠查看堆內存各部分的使用量,以及加載類的數量。命令的格式以下 :
jstat[-命令選項][vmid][間隔時間/毫秒][查詢次數]
6.4.1 查看class加載統計

說明 : Loaded : 加載class的數量 Bytes : 所佔用空間大小 Unloaded : 未加載數量 Bytes : 未加載佔用空間 Time : 時間程序員

6.4.2 查看編譯統計

jstat -compiler  數組

說明 : Compiled : 編譯數量 Failed : 失敗數量 Invalid : 不可用數量 Time : 時間 FailedType : 失敗類型 FailedMethod : 失敗的方法瀏覽器

6.4.3 垃圾回收統計

jstat -gc 緩存

也能夠指定打印的間隔和次數,每1秒中打印一次,共打印5次 jstat -gc 1000 5 tomcat

說明 : S0C : 第一個Survivor區的大小(KB) S1C : 第二個Survivor區的大小(KB) S0U : 第一個Survivor區的使用大小(KB) S1U : 第二個Survivor區的使用大小(KB) EC : Eden區的大小(KB) EU : Eden區的使用大小(KB) OC : Old區大小(KB) OU : Old使用大小(KB) MC :方法區大小(KB) MU :方法區使用大小(KB) CCSC :壓縮類空間使用大小(KB) CCSU :壓縮類空間使用大小(KB) YGC :年輕代垃圾回收次數 YGCT :年輕代垃圾回收消耗時間 FGC :老年代垃圾回收次數 FGCT : 老年代垃圾回收消耗時間 GCT :垃圾回收消耗總時間服務器

7.1 查詢內存使用狀況

前面經過jstat能夠對jvm堆的內存進行統計分析,而jmap能夠獲取到更加詳細的內容,如 :內存使用狀況的彙總、對內存溢出的定位與分析。
查看內存使用狀況 :
jmap -heap <vmid>

7.2 查看內存中對象數量及大小

查看全部對象,包括活躍以及非活躍的
jmap -histo <pid> | more
查看活躍對象
jmap -histo:live <pid> | more

對象說明 : B : byte C : char D : double F : float I : int J : long Z : boolean [ : 數組,如[I表示int[] [L+類名 :其餘對象網絡

7.3 將內存使用狀況dump到文件中

有些時候咱們須要將jvm當前內存中的狀況dump到文件中,而後對它進行分析,jmap也是支持dump到文件中的。
用法 :
jmap -dump:format=b,file=dumpFileName <pid>
其中b是表明二進制
示例 :
jmap -dump:format=b,file=/tmp/dump.dat 6219

7.4 經過jhat對dump文件進行分析

用法 :
jhat -port <port> <file>
例如 :
jhat -port 9999 /test/dump.dat
這個時候就能夠打開瀏覽器訪問 :127.0.0.2:9999	
-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError : 設置初始堆大小和當發生內存溢出時,給內存一個快照並導出一個dump文件	
用MAT進行文件分析

8 jstack的使用

有些時候咱們須要查看下jvm中的線程執行狀況,好比,發現服務器的CPU的負載忽然增高了、出現了死鎖、死循環等,咱們
如何分析呢?
因爲程序是正常運行的,沒有任何的輸出,從日誌方面也看不出什麼問題,因此就須要看下jvm的內部線程的執行狀況,而後
再進行分析查找出緣由。
這個時候,就須要藉助於jstack命令了,jstack的做用是將正在運行的jvm的線程狀況進行快照,而且打印出來 :
用法 :jstack <pid>

8.1 線程的狀態

在java中線程的狀態一共被分紅6種 :
	初始態(NEW)
		建立一個Thread對象,但還未調用start()啓動線程時,線程處於初始態。
	運行態(RUNNABLE),在java中,運行態包括就緒態和運行態。	
		就緒態
			該狀態下的線程已經得到執行所需的全部資源,只要CPU分配執行權就能運行。
			全部就緒態的線程存放在就緒隊列中。
		運行態
			得到CPU執行權,正在執行的線程。
			因爲一個CPU同一時刻只能執行一條線程,所以每一個CPU每一個時刻只有一條運行態的線程。
	阻塞態(BLOCKED)
			當一條正在執行的線程請求某一資源失敗時,就會進入阻塞態。
			而在java中,阻塞態專指請求鎖失敗時進入的狀態。
			由一個阻塞隊列存放全部阻塞態的線程。
			處於阻塞態的線程會不斷請求資源,一旦請求成功,就會進入就緒隊列,等待執行。
	等待態(WAITING)
			當前線程中調用wait、join、park函數時,當前線程就會進入等待態。
			也有一個等待隊列存放全部等待態的線程。
			線程處於等待態表示它須要等待其餘線程的指示才能繼續運行。
			進入等待態的線程會釋放CPU執行權,並釋放資源(如 :鎖)。
	超時等待態(TIMED_WAITING)
			當運行中的線程調用sleep(time)、wait、join、parkNanos、parkUntil時,就會進入該狀態;
			它和等待態同樣,並非由於請求不到資源,而是主動進入,而且進入後須要其餘線程喚醒;
			進入該狀態後釋放CPU執行權和佔有的資源。
			與等待態的區別:到了超時時間後自動進入阻塞隊列,開始競爭鎖。
	終止態(TERMINATED)
			線程執行結束後的狀態。

8.2 分析死鎖

在運行的程序中,經過命令窗口查看當前正在執行的線程id			
jps
同經過jstack進行分析 :
jstack <vmid>

在輸出的信息中,已經看到,發現了1個死鎖,關鍵看這個 : app

能夠清晰的看到 :
	Thread2 獲取了<0x00000000f655dc50>的鎖,等待獲取<0x00000000f655dc40>這個鎖
	Thread1 獲取了<0x00000000f655dc40>的鎖,等待獲取<0x00000000f655dc50>這個鎖
	因而可知,發生了死鎖。
可使用VisualVM工具進行JVM問題的排查

8.3 監控遠程的jvm

VisualJVM不只是能夠監控本地jvm進程,還能夠監控遠程的jvm進程,須要藉助於JMX技術實現。
8.3.1 什麼是JMX?
JMX(Java Management Extensions,即Java管理擴展)是一個爲應用程序、設備、系統等植入管理功能的框架,JMX能夠跨越一系列操做
平臺、系統體系結構和網絡傳輸協議,靈活的開發無縫集成的系統、網絡和服務管理應用。
8.3.2 監控遠程的tomcat
想要監控遠程的tomcat,就須要在遠程的tomcat進行對JMX配置,方法以下 :
#在tomcat的bin目錄下,修改catalina.sh,添加以下的參數
JAVA_OPTS="
-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=9999 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmremote.ssl=false"																
這幾個參數的意思是 :
#-Dcom.sun.management.jmxremote : 容許使用JMX遠程管理
#-Dcom.sun.management.jmxremote.port=9999  : JMX遠程鏈接端口
#-Dcom.sun.management.jmxremote.authenticate=false : 不進行身份認證,任何用戶均可以鏈接
#-Dcom.sun.management.jmremote.ssl=false : 不使用ssl
設置好之後保存,並重啓tomcat
./startup.sh && tail -f ../logs/catalina.out  :  重啓tomcat並顯示啓動日誌
經過VisualVM進行遠程鏈接。
SpringBoot項目是內嵌的tomcat配置方式查看http://www.360doc.com/content/17/1018/20/16915_696185383.shtml

點關注,不迷路,這是一個程序員都想要關注的公衆號

相關文章
相關標籤/搜索