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


1.jvm的運行參數

在jvm中有很多的參數可以進行設置,這樣可以讓jvm在各種環境中都能夠高效的運行。絕大部分的參數保持默認即可。

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的永久代最大的
區別所在。
在這里插入圖片描述

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
在這里插入圖片描述
說明 :
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個死鎖,關鍵看這個 :
在這里插入圖片描述

可以清晰的看到 :
	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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM