1.了解JVM常用命令行參數
HotSpot參數分類
標准: - 開頭,所有的HotSpot都支持
非標准:-X 開頭,特定版本HotSpot支持特定命令
不穩定:-XX 開頭,下個版本可能取消
java -XX:+PrintFlagsFinal | grep xxx 找到對應的參數
java -XX:+PrintFlagsFinal -version |grep GC
參考鏈接:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
2.調優前的基礎概念
1.吞吐量:用戶代碼時間 /(用戶代碼執行時間 + 垃圾回收時間)。吞吐量優先的一般:(PS + PO)
2.響應時間:STW越短,響應時間越好。響應時間優先用G1
3.什么是調優
1.根據需求進行JVM規划和預調優
2.優化JVM運行環境(慢,卡頓)
3.解決JVM運行過程中出現的各種問題(OOM)
4.調優從規划開始
-
調優,從業務場景開始,沒有業務場景的調優都是耍流氓
-
無監控(壓力測試,能看到結果),不調優
-
步驟
1.熟悉業務場景(沒有最好的垃圾回收器,只有最合適的垃圾回收器)
1.響應時間、停頓時間 [CMS G1 ZGC] (需要給用戶作響應)
2.吞吐量 = 用戶時間 /( 用戶時間 + GC時間) [PS]
2.選擇回收器組合
3.計算內存需求(經驗值)
4.選定CPU(越高越好)
5.設定年代大小、升級年齡(一般不需要設置)
6.設定日志參數
1.-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause (gc日志文件路徑,循環打印,一共5個日志文件,每個文件大小20mb,打印gc詳細信息,發生gc的時間和原因)
2.或者每天產生一個日志文件
7.觀察日志情況
-
案例1:垂直電商,最高每日百萬訂單,處理訂單系統需要什么樣的服務器配置?
很業余的問法
這種情況一般要估算業務最高峰值(比如5000訂單/秒),根據最高峰值來估算系統資源,然后壓測
-
案例2:12306遭遇春節大規模搶票應該如何支撐?
12306應該是中國並發量最大的秒殺網站,號稱並發量100W最高
CDN -> LVS -> NGINX -> 業務系統 -> 每台機器1W並發(10K問題) 100台機器
大流量的處理方法:分而治之
5.優化環境
-
有一個50萬PV的資料類網站(從磁盤提取文檔到內存)原服務器32位,1.5G的堆,用戶反饋網站比較緩慢,因此公司決定升級,新的服務器為64位,16G的堆內存,結果用戶反饋卡頓十分嚴重,反而比以前效率更低了
1.為什么原網站慢:很多用戶瀏覽數據,很多數據load到內存,內存不足,頻繁GC,STW長,響應時間變慢
2.升級服務器后為什么會更卡頓:內存越大,FGC時間越長
3.如何優化:PS+PO -> PN + CMS 或者 G1,最直接的應該是從業務上優化,不要把文檔加載到內存,而是用一個文件服務器之類的
-
系統CPU經常100%,如何調優?(面試高頻)
1.CPU100%那么一定有線程在占用系統資源
2.找出哪個進程cpu高(top)
3.該進程中的哪個線程cpu高(top -Hp)
4.導出該線程的堆棧 (jstack)
5.查找哪個方法(棧幀)消耗時間 (jstack)
6.觀察是工作線程占比高 | 垃圾回收線程占比高
-
系統內存飆高,如何查找問題?(面試高頻)
1.導出堆內存 (jmap)
2.分析 (jhat jvisualvm mat jprofiler ... )
-
如何監控JVM
jstat jvisualvm jprofiler arthas top...
6.解決JVM運行中的問題
-
一個案例理解常用工具
1.測試代碼:
import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 從數據庫中讀取信用數據,套用模型,並把結果進行記錄和傳輸 */ public class T15_FullGC_Problem01 { private static class CardInfo { BigDecimal price = new BigDecimal(0.0); String name = "張三"; int age = 5; Date birthdate = new Date(); public void m() {} } private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50, new ThreadPoolExecutor.DiscardOldestPolicy()); public static void main(String[] args) throws Exception { executor.setMaximumPoolSize(50); for (;;){ modelFit(); Thread.sleep(100); } } private static void modelFit(){ List<CardInfo> taskList = getAllCardInfo(); taskList.forEach(info -> { // do something executor.scheduleWithFixedDelay(() -> { //do sth with info info.m(); }, 2, 3, TimeUnit.SECONDS); }); } private static List<CardInfo> getAllCardInfo(){ List<CardInfo> taskList = new ArrayList<>(); for (int i = 0; i < 100; i++) { CardInfo ci = new CardInfo(); taskList.add(ci); } return taskList; } }
2.java -Xms200M -Xmx200M -XX:+PrintGC T15_FullGC_Problem01
3.一般是運維團隊首先受到報警信息(CPU Memory)
4.top命令觀察到問題:內存不斷增長 CPU占用率居高不下
5.top -Hp 觀察進程中的線程,哪個線程CPU和內存占比高
6.jps定位具體java進程;jstack 定位線程狀況,重點關注:WAITING BLOCKED
eg.waiting on <0x0000000088ca3310> (a java.lang.Object)
假如有一個進程中100個線程,很多線程都在waiting on,一定要找到是哪個線程持有這把鎖.怎么找?搜索jstack dump的信息,找 ,看哪個線程持有這把鎖RUNNABLE 7.為什么阿里規范里規定,線程的名稱(尤其是線程池)都要寫有意義的名稱
怎么樣自定義線程池里的線程名稱?(自定義ThreadFactory)
8.jinfo pid
9.jstat -gc 動態觀察gc情況 / 閱讀GC日志發現頻繁GC / arthas觀察 / jconsole/jvisualVM/ Jprofiler(最好用)
jstat -gc 4655 500 : 每個500個毫秒打印GC的情況
如果面試官問你是怎么定位OOM問題的?如果你回答用圖形界面(錯誤)
1:已經上線的系統不用圖形界面用什么?(cmdline arthas)
2:圖形界面到底用在什么地方?測試!測試的時候進行監控!(壓測觀察)10.jmap - histo 4655 | head -20,查找有多少對象產生
11.jmap -dump:format=b,file=xxx pid :
線上系統,內存特別大,jmap執行期間會對進程產生很大影響,甚至卡頓(電商不適合)
1:設定了參數HeapDump,OOM的時候會自動產生堆轉儲文件
2:很多服務器備份(高可用),停掉這台服務器對其他服務器不影響
3:在線定位(一般小點兒公司用不到)12.java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError T15_FullGC_Problem01
13.使用MAT / jhat /jvisualvm 進行dump文件分析
https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
jhat -J-mx512M xxx.dump
http://192.168.17.11:7000
拉到最后:找到對應鏈接
可以使用OQL查找特定問題對象14.找到代碼的問題
-
jconsole遠程連接
1.程序啟動加入參數:
java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=11111 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false XXX
2.如果遭遇 Local host name unknown:XXX的錯誤,修改/etc/hosts文件,把XXX加入進去
192.168.17.11 basic localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
3.關閉linux防火牆(實戰中應該打開對應端口)
service iptables stop chkconfig iptables off #永久關閉
4.windows上打開 jconsole遠程連接 192.168.17.11:11111
-
jvisualvm遠程連接
-
jprofiler (收費)
-
arthas在線排查工具
為什么需要在線排查:在生產上我們經常會碰到一些不好排查的問題,例如線程安全問題,用最簡單的threaddump或者heapdump不好查到問題原因。為了排查這些問題,有時我們會臨時加一些日志,比如在一些關鍵的函數里打印出入參,然后重新打包發布,如果打了日志還是沒找到問題,繼續加日志,重新打包發布。對於上線流程復雜而且審核比較嚴的公司,從改代碼到上線需要層層的流轉,會大大影響問題排查的進度。
jvm觀察jvm信息
thread定位線程問題
dashboard 觀察系統情況
heapdump + jhat分析
jad反編譯
動態代理生成類的問題定位
第三方的類(觀察代碼)
版本問題(確定自己最新提交的版本是不是被使用)redefine 熱替換
目前有些限制條件:只能改方法實現(方法已經運行完成),不能改方法名, 不能改屬性
sc - search class
watch - watch method
沒有包含的功能:jmap