先來看一下java 虛擬機的各個含義html
-Xms 堆的初始值 -Xmx 堆的最大值 -Xmn 堆中新生代的值 -XX:MetaspaceSize 元數據區值 -XX:MaxMetaspaceSize 元數據區最大值 -Xss 線程空間大小 -XX:SurvivorRatio 新生代中 伊甸區與s0 和s1的比例 -XX:NewRatio 老年代與新生代的比例
上述內容有以下注意:java
堆包括 新生代和老年代。 新生代 包括 伊甸區 、s0、s1 。 元數據區佔用堆外內存,直接使用物理內存,若是不設置最大值,則會無限上漲。 -xss大小爲線程空間,使用的也是 堆外內存,直接佔用物理內存,在物理內存必定量的狀況下:該值越大,則整個系統可以使用的線程越少,但棧的遞歸深度會增長。
定位問題:linux
場景: 一個桌面的java swing程序,是一個工具大集合,再點擊了其中的幾個工具後,發現整個工具開始卡頓。 先看一下拉起時的bat腳本中的java參數:安全
set JVM_OPTS=-Xms32m -Xmx64m -Xmn16m -XX:MetaspaceSize=32m -XX:MaxMetaspaceSize=32m網絡
因爲是一個桌面的程序,因此,沒有給太多的內存,根據參數,能夠得出各個區域的大小爲:jvm
最大堆時爲 64Mxss
老年代爲 48M工具
新生代爲 16M性能
元數據區 爲32Mspa
對於性能問題,有兩個定位思路: 一個是沒有內存卡,第二個是cpu被佔用卡,三是看網絡卡不卡(和網絡相關)
好,那咱們一個一個看,首先咱們先看cpu被佔用卡頓
從window 任務管理器 查看到咱們啓動java程序,看一下 程序 佔用的cpu,若是很高,則說明是線程出了問題。 那咱們須要將java進程找到究竟是哪一個線程 出的問題,此時須要藉助工具ProcessExplorer 來查看線程
打開工具,找到對應的進程,右擊 屬性,在thread 頁面,能夠看到 線程的id。
此時,咱們須要先借助jstack 進程id 命令,打印出java 進程中素有的線程,
下一步,咱們要講以前看到的cpu使用很高的線程id 變成16進制,而後在 jstack 線程 信息中,尋找nid 爲該值的線程,根據線程的信息,肯定究竟是哪一個類除了問題。
若是是linux系統的話,這能夠經過top 產看進程 top -Hp 進程id 產看線程id,後面處理就和window的同樣了。
程序卡住不動時,咱們先用 jstat -gc 進程id 來獲取 當前的 內存使用狀況以下
C:\Windows\system32>jstat -gc 4848 1000 20 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874 512.0 512.0 0.0 0.0 15360.0 2.0 49152.0 10068.4 32768.0 32079.0 3712.0 3601.6 3150 4.263 2943 110.611 114.874
其中 4848 是進程id,1000爲1s採集一次 20表明採集20次
須要關注的是,這個地方列出的 單位是KB 其中
s0c表明新生代的s0 區分配0.5M的內存 s1c表明新生代的s1 區分配0.5M的內存 s0u表明新生代的s0 區使用了0 內存 s1u表明新生代的s1 區使用了0 內存 EC表明新生代伊甸區爲15M EU表明新生代伊甸區使用了2KB OC OU 表明老年代 分配空間和使用狀況,當前分配了48M,用了10M CCSC:壓縮類空間大小 CCSU:壓縮類空間使用大小 YGC:年輕代垃圾回收次數 YGCT:年輕代垃圾回收消耗時間 FGC:老年代垃圾回收次數 FGCT:老年代垃圾回收消耗時間 GCT:垃圾回收消耗總時間
重點關注 的是 MC和MU 這兩列的值,發如今程序卡頓的時候,這個值很是接近咱們對jvm 設置參數時候的最大值32M
也就是說:元數據區 的空間不夠了!
那麼這個元數據區 是幹啥的? 其做用和jdk7及其以前的版本中的方法區同樣,是用來存儲 類信息,固然jdk8 以後 叫元數據區了,和jdk7有區別。
竟然是加載的類太多了?
那如何看到jvm加載了那些類呢?
在jvm的參數上面加上 -verbose:class 能夠看到加載了那些類 推薦 將 結果 保存到文件,而後分析
可是你會發現 有些類名 被壓縮了,根本看不出來,此時,你須要再加上一個參數
-Dcom.sun.xml.bind.v2.bytecode.ClassTailor.noOptimize= true
此命令也能夠表示直接關閉bytecode反射機制
加上jvm參數後,能夠繼續運行程序,看一下究竟是加載了那些類,致使的這個問題。
後面定位分析,就須要你結合自身的代碼,肯定究竟是哪一個類,加載過多,因爲公司安全,我沒法上代碼了。
最後:這裏再補充一下,在發生性能問題的時候,應該說100%的問題是cpu使用率問題形成的,而堆內存不夠或者是元數據內存不夠,只是 java卡頓在內存上的黑盒表現,在卡頓時,經過監控java 進程中線程的使用率,你會發現,cpu 很高的每每是 GC線程,這時,你要關注的本質上,仍是內存不夠了。
最後,附上兩位大神的定位記錄,很是棒: