jvm分析&調優

                                               jvm分析&調優

在平時的java開發工作中,特別是將應用部署到服務器之後,經常會出現各種各樣的問題,例如內存泄漏、死鎖、CPU飆高等。下面我們就來學習掌握一些工具來分析到底是哪裏出了問題,做到及時定位問題、有效解決問題。

一、空間查看

1.1 磁盤空間

df --help 查看命令使用幫助說明


df -h #查看磁盤空間大小

du -sh #查看當前文件夾所有文件大小

du -h /home #查看指定文件夾大小

du -h /home/ #查看指定文件夾下所有文件的大小

du -h login.log #查看指定文件大小

df /home #查看目錄掛載點

df /home -kh #加上-kh以g單位顯示

du -sh ./* #統計當前目錄各文件夾大

du /home/ -h --max-depth=1 #查看home文件夾下文件大小

1.2 內存空間

free [options],默認情況下,即在沒有選項的情況下,"free"命令顯示內存的使用信息。默認按照k(b)的計數單位統計。

free -b/k/m/g/h ( free -h:以適於人類可讀方式顯示內存信息。-h與其他命令最大不同是-h選項會在數字後面加上適於人類可讀的單位)

free -s N:表示每隔n秒打印一次內存信息,直到用ctrl+c結束

free -c N:表示重複打印內存信息n次

free -hs N: 以人類可讀的方式,每隔ns打印一次內存信息,直到ctrl+c結束

free -s N -c M:每隔n秒打印一次內存信息,共打印m次
  • total:表示 總計物理內存的大小。
  • used:表示 已使用多少。
  • free:表示 可用內存多少。
  • Shared:表示多個進程共享的內存總額。
  • Buffers/cached:表示 磁盤緩存的大小。

二、進程確定

2.1 查看比較耗內存的進程

top //shift+m 按內存佔用比排序

參數含義

第一行:
21:31:22 當前系統時間
up 16 days,  6:03 系統已經運行了16天6小時03分鐘(在這期間沒有重啓過)
2 users 當前有2個用戶登錄系統
load average: 0.03, 0.06, 0.05  load average後面的三個數分別是1分鐘、5分鐘、15分鐘的負載情況。load average數據是每隔5秒鐘檢查一次活躍的進程數,然後按特定算法計算出的數值。如果這個數除以邏輯 CPU的數量,結果高於5的時候就表明系統在超負荷運轉了。

第二行: Tasks 任務(進程)
系統現在共有87個進程,其中 running(運行) 2個,sleep(休眠)85 個,stopped 0個,zombie(殭屍) 1個。

第三行:cpu狀態
2.7% us 用戶空間佔用CPU的百分比。
3.0% sy 內核空間佔用CPU的百分比。
0.0% ni 改變過優先級的進程佔用CPU的百分比
94.4% id 空閒CPU百分比
0.0% wa IO等待佔用CPU的百分比
0.0% hi 硬中斷(Hardware IRQ)佔用CPU的百分比
0.0% si 軟中斷(Software Interrupts)佔用CPU的百分比

第四行:內存狀態
1014864k total 物理內存總量(991M)
80320k free 空閒內存總量(79M)
753356k used 使用中的內存總量(735M)
179808k buffers/cached 緩存的內存量 (175M)

使用中的內存總量(used)指的是現在系統內核控制的內存數,空閒內存總量(free)是內核還未納入其管控範圍的數量。納入內核管理的內存不見得都在使用中,還包括過去使用過的現在可以被重複利用的內存,內核並不把這些可被重新使用的內存交還到free中去,因此在linux上free內存會越來越少,但不用爲此擔心。

如果出於習慣去計算可用內存數,這裏有個近似的計算公式:第四行的free + 第四行的buffers + 第五行的cached,按這個公式此臺服務器的可用內存: 79 +175 + 99 = 353M。

對於內存監控,在top裏我們要時刻監控第五行swap交換分區的used,如果這個數值在不斷的變化,說明內核在不斷進行內存和swap的數據交換,這是真正的內存不夠用了。

第五行:swap交換分區
0k total 交換區總量(0M)
0k used 使用的交換區總量(0M)
0k free 空閒交換區總量(0M)
101588k avail Mem 緩衝的交換區總量(99M)

第七行以下:各進程(任務)的狀態監控
PID 進程id
USER 進程所有者
PR 進程優先級
NI nice值。負值表示高優先級,正值表示低優先級
VIRT 進程使用的虛擬內存總量,單位kb。VIRT=SWAP+RES
RES 進程使用的、未被換出的物理內存大小,單位kb。RES=CODE+DATA
SHR 共享內存大小,單位kb
S 進程狀態。D=不可中斷的睡眠狀態 R=運行 S=睡眠 T=跟蹤/停止 Z=殭屍進程
%CPU 上次更新到現在的CPU時間佔用百分比
%MEM 進程使用的物理內存百分比
TIME+ 進程使用的CPU時間總計,單位1/100秒
COMMAND 進程名稱(命令名/命令行)

2.2 查看java進程

1.jps:虛擬機進程狀況工具

jps主要用來輸出JVM中運行的進程狀態信息。語法格式如下:
jps [options] [hostid]

第一個參數:options
   -q 不輸出類名、Jar名和傳入main方法的參數
   -m 輸出傳入main方法的參數
   -l 輸出main類或Jar的全限名
   -v 輸出傳入JVM的參數

第二個參數:hostid
主機或者是服務器的id,如果不指定,就默認爲當前的主機或者是服務器。

2.ps -ef|grep tomcat 或者 ps -aux|grep tomcat

三、jvm 性能監控工具
       工具主要是爲了解決問題而生的,就是由於我們的程序存在着一些性能問題,纔有了這些工具。其實當我們在下載完成JDK,裏面就包含了一些性能檢測工具,在JDK安裝目錄下有很多性能監控工具。

  1. jps:虛擬機進程狀況工具
  2. jstat:虛擬機統計信息監視工具
  3. jmap:Java內存印象工具
  4. jhat:虛擬機堆轉儲快照分析工具
  5. jstack:Java堆棧跟蹤工具
  6. jinfo:Java配置信息工具

1、jps:虛擬機進程狀況工具
jps主要用來輸出JVM中運行的進程狀態信息。語法格式如下:
jps [options] [hostid]
第一個參數:options
-q 不輸出類名、Jar名和傳入main方法的參數
-m 輸出傳入main方法的參數
-l 輸出main類或Jar的全限名
-v 輸出傳入JVM的參數
第二個參數:hostid
主機或者是服務器的id,如果不指定,就默認爲當前的主機或者是服務器。

2、jstack:堆棧跟蹤工具
jstack用於生成虛擬機當前時刻的線程快照。語法格式如下:
jstack [option] vmid
第一個參數:option
第二個參數:vmid
vmid是Java虛擬機ID,在Linux/Unix系統上一般就是進程ID。


3、jstat:虛擬機統計信息監控工具
jstat監視虛擬機各種運行狀態信息,可以顯示本地或者是遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。語法格式如下:
jstat [ generalOption | outputOptions vmid [interval] [count]] ]
第一個參數:generalOption | outputOptions
這個參數表示的option,代表着用戶希望查詢的虛擬機信息,分爲類加載、垃圾收集、運行期編譯狀況3類。
第二個參數:vmid
vmid是Java虛擬機ID,在Linux/Unix系統上一般就是進程ID。
第三個參數:interval
interval是採樣時間間隔,
第四個參數:count
count表示的是採樣數。

4、jinfo:實時地查看和調整虛擬機各項參數
命令格式:
jinfo [option] pid
第一個參數:option
第二個參數:pid
指定顯示的進程id。

5、jmap:生成虛擬機的內存轉儲快照(heapdump文件)
jmap(Memory Map for Java,內存映像工具),用於生成堆轉存的快照,一般是heapdump或者dump文件。如果不適用jmap命令,可以使用-XX:+HeapDumpOnOutOfMemoryError參數,當虛擬機發生內存溢出的時候可以產生快照。或者使用kill -3 pid也可以產生。jmap的作用並不僅僅是爲了獲取dump文件,它可以查詢finalize執行隊列,java堆和永久代的詳細信息,如空間使用率,當前用的哪種收集器。命令格式如下:
jmap [option] vmid
第一個參數:
第二個參數:vmid
vmid是Java虛擬機ID,在Linux/Unix系統上一般就是進程ID.

6、jhat:分析內存轉儲快照,不推薦使用,而且慢
由於這個工具功能比較簡陋,運行起來也比較耗時,所以這個工具不推薦使用,推薦使用MAT。

7、JConsole:JMX的可視化管理工具
這個工具相比較前面幾個工具,使用率比較高,很重要。它是一個java GUI監視工具,可以以圖表化的形式顯示各種數據。並可通過遠程連接監視遠程的服務器VM。用java寫的GUI程序,用來監控VM,並可監控遠程的VM,非常易用,而且功能非常強。
在cmd裏面輸入 jconsole,選則進程就可以了。(前提是在IDE工具先建立一個線程運行着)
然後我們選擇了相應的選項之後,進入這個工具就會出現下面這個界面
在上面有菜單,我們可以選擇其中一個進行查看,就可以了,這個用具用起來很方便,也是我之前用的比較多的工具。

8、VisualVM:多合一故障管理工具
這個工具也很牛bility。它同jconsole都是一個基於圖形化界面的、可以查看本地及遠程的JAVA GUI監控工具,Jvisualvm同jconsole的使用方式一樣,直接在命令行打入jvisualvm即可啓動,jvisualvm界面更美觀一些,數據更實時。

四、jvm調優

4.1 配置參數

在tomcat/bin/catalina.sh的配置開始位置添加:

JAVA_OPTS="-Djava.awt.headless=true -XX:+PrintGCDetails -Dfile.encoding=UTF-8 -server -Xms128m -Xmx512m -XX:NewSize=1m -XX:MaxNewSize=256m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=256m"

參數說明:

-Xms 初始堆的分配大小,默認爲物理內存的六十四分之一
-Xmx 堆的最大分配大小(默認爲物理內存的四分之一),在實際工作中,可以直接將初始的堆大小與最大堆大小相等,減少程序運行時垃圾回收的次數,提高效率。
如:-Xms512m -Xmx512m
-Xmn 新生代堆最大可用值(默認爲堆內存的64分之一)
-XX:NewSize和-XX:MaxNewSize 用於設置年輕代的大小,建議設爲整個堆大小的1/3或者1/4,兩個值設爲一樣大。
-XX:+PrintGC 每次觸發GC的時候打印相關日誌
-XX:+PrintGCDetails 打印詳細的GC日誌
-XX:+UseSerialGC 串行回收
-XX:SurvivorRatio
含以-XX:SurvivorRatio=eden/from=den/to

用來設置新生代中rom:to:eden空間的比例 (默認1:1:8,即-XX:SurvivorRatio=8)

在實際工作中,我們可以直接將初始的堆大小與最大堆大小相等,這樣的好處是可以減少程序運行時垃圾回收次數,從而提高效率。

-XX:NewRatio

設置年輕代和年老代的比值(默認爲2)。如:爲3,表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代和的1/4

-Xss 每個線程堆棧的大小。一般情況下256K是足夠了,會影響此進程中併發線程數大小。

JVM參數調優總結
在JVM啓動參數中,可以設置跟內存、垃圾回收相關的一些參數設置,默認情況不做任何設置JVM會工作的很好,但對一些配置很好的Server和具體的應用必須仔細調優才能獲得最佳性能。通過設置我們希望達到一些目標:

  • GC的時間足夠的小
  • GC的次數足夠的少
  • 發生Full GC的週期足夠的長

前兩個目前是相悖的,要想GC時間小必須要一個更小的堆,要保證GC次數足夠少,必須保證一個更大的堆,我們只能取其平衡。

  • 針對JVM堆的設置,一般可以通過-Xms -Xmx限定其最小、最大值,爲了防止垃圾收集器在最小、最大之間收縮堆而產生額外的時間,我們通常把最大、最小設置爲相同的值。
  • 年輕代和年老代將根據默認的比例(1:2)分配堆內存,可以通過調整二者之間的比率NewRadio來調整二者之間的大小,也可以針對回收代,比如年輕代,通過 -XX:newSize -XX:MaxNewSize來設置其絕對大小。同樣,爲了防止年輕代的堆收縮,我們通常會把-XX:newSize -XX:MaxNewSize設置爲同樣大小
  • 年輕代和年老代設置多大才算合理?這個我問題毫無疑問是沒有答案的,否則也就不會有調優。我們觀察一下二者大小變化有哪些影響:
  1. 更大的年輕代必然導致更小的年老代,大的年輕代會延長普通GC的週期,但會增加每次GC的時間;
  2. 小的年老代會導致更頻繁的Full GC,更小的年輕代必然導致更大年老代,小的年輕代會導致普通GC很頻繁,但每次的GC時間會更短;大的年老代會減少Full GC的頻率。
  3. 如何選擇應該依賴應用程序對象生命週期的分佈情況:如果應用存在大量的臨時對象,應該選擇更大的年輕代;如果存在相對較多的持久對象,年老代應該適當增大。

但很多應用都沒有這樣明顯的特性,在抉擇時應該根據以下兩點:(A)本着Full GC儘量少的原則,讓年老代儘量緩存常用對象,JVM的默認比例1:2也是這個道理 (B)通過觀察應用一段時間,看其他在峯值時年老代會佔多少內存,在不影響Full GC的前提下,根據實際情況加大年輕代,比如可以把比例控制在1:1。但應該給年老代至少預留1/3的增長空間。

4.2 怎麼樣避免發生內存泄露和溢出

  • a、儘早釋放無用對象的引用
  • b、使用字符串處理,避免使用String,應大量使用StringBuffer,每一個String對象都得獨立佔用內存一塊區域
  • c、儘量少用靜態變量,因爲靜態變量存放在永久代(方法區),永久代基本不參與垃圾回收
  • d、避免在循環中創建對象
  • e、開啓大型文件或從數據庫一次拿了太多的數據很容易造成內存溢出,所以在這些地方要大概計算一下數據量的最大值是多少,並且設定所需最小及最大的內存空間值。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章