本系列是用來記錄《深刻理解Java虛擬機》這本書的讀書筆記。方便本身查看,也方便你們查閱。html
欲速則不達,欲達則欲速!前端
第四章 虛擬機性能監控與故障處理工具總結java
1、JDK的命令行工具面試
一、jps:虛擬機進程情況工具算法
能夠列出正在運行的虛擬機進程,並顯示虛擬機執行主類(main所在類)名稱以及這些進程的本地虛擬機惟一ID(Local Virtual Machine Identifier LVMID)瀏覽器
二、jstat:虛擬機統計信息監視工具緩存
jstat(JVM Statistics Monitoring Tool)是用於監視虛擬機各類運行狀態信息的命令行工具,它能夠顯示本地或者遠程虛擬機進程中的類加載、內存、垃圾收集、JIT編譯等運行數據,在沒有GUI圖形界面,只提供純文本控制檯環境的服務器上,它將時運行期定位虛擬機性能問題的首選工具。服務器
三、jinfo:java配置信息工具session
jinfo(Configuration Info for java)的做用是實時地查看和調整虛擬機各項參數併發
四、jmap:java內存映象工具
jmap(Merory Map for java)命令用於生成堆轉儲快照(通常稱爲heapdump或dump文件)
五、jhat:虛擬機堆轉儲快照分析工具
Sun JDK提供jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,分析jmap生成的堆轉儲快照jhat內置了一個微型的HTTP/HTML服務器,生成dump文件的分析結果後,能夠在瀏覽器中查看。
六、jstack:java堆棧跟蹤工具
jstack(Stack Trace for java)命令用於生成虛擬機當前時刻的線程快照(通常稱爲threaddump或者Javacore文件),線程快照就是當前虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的緣由,如線程間死鎖、死循環、請求外部資源致使的長時間等待等都是致使線程長時間停頓的常見緣由,線程出現停頓的時候經過jstack來查看各個線程的調用堆棧,就能夠知道沒有響應的線程到底在後臺作了些什麼事情,或者等待着什麼資源。
在JDK1.5中java.lang.Thread類新增了一個getAllStackTreces()方法,用於獲取虛擬機中全部線程的StackTraceElement對象,使用這個方法能夠經過簡單的幾行代碼就完成jstack的大部分功能,在實際項目中不妨調用這個方法作個管理員頁面,能夠隨時使用瀏覽器來查看線程堆棧。
<%@ page import="java.util.Map"%> <html> <head> <title>服務器線程信息</title> </head> <body> <pre> <% for (Map.Entry<Thread, StackTraceElement[]> stackTrace : Thread.getAllStackTraces().entrySet()) { Thread thread = (Thread) stackTrace.getKey(); StackTraceElement[] stack = (StackTraceElement[]) stackTrace.getValue(); if (thread.equals(Thread.currentThread())) { continue; } out.print("\n線程:" + thread.getName() + "\n"); for (StackTraceElement element : stack) { out.print("\t"+element+"\n"); } } %> </pre> </body> </html>
七、HSIDS:JIT生成代碼反彙編
HSIDS是一個Sun官方推薦的HotSpot虛擬機JIT編譯代碼的反彙編插件
2、JDK的可視化工具
一、JConsole:java監視與管理控制檯
JConsole(java monitoring and management console)是一種基於JMX的可視化監視管理工具,它管理部分的功能是針對JMX MBean進行管理,MBean可使用代碼,中間件服務器的管理控制檯或者符合JMX規範的軟件進行訪問。
二、VisualVM:多合一故障處理工具
VisualVM(All-in-One java Troubleshooting Tool)是到目前爲止隨JDK發佈的功能最強大的運行監視和故障處理程序,而且能夠預見在將來一段時間內都是官方主力發展的虛擬機故障處理工具,官方在VisualVM的軟件說明上寫上了「All-in-One」的描述字樣,預示着它除了運行監視、故障處理外,還提供了不少其餘方面的功能,如性能分析,VisualVM的性能分析功能甚至比起JProfiler YourKit等專業且收費的Profiling工具都不會遜色多少,而且VisualVM還有一個很大的優勢:不須要被監視的程序基於特殊Agent運行,所以它對應用程序的實際性能的影響很小,使得它能夠直接應用在生產環境中,這個優勢是JProfiler YourKit等工具沒法與之媲美的。
第五章 調優案例分析與實戰
在《深刻理解java虛擬機》書中本節的主要內容是講做者在工做過程當中對調優的一些經驗實戰。對於咱們讀者來講,重點是學習做者分析解決問題的具體思路。
1、編譯時間和類加載時間的優化
編譯時間是指虛擬機的JTI編譯器編譯熱點代碼的耗時。
虛擬機內置了兩個運行時編譯器,若是java方法被調用次數達到必定程序,就會被斷定爲熱代碼交給JIT編譯器,即時編譯爲本地代碼,提升運行速度,這就是Hotspot虛擬機名字的由來。
2、高性能硬件上的程序部署策略
一、基本信息簡介
(1)環境:一個15萬PV/天左右的在線文檔類型網站最近更換了硬件系統,新系統硬件爲4個CPU、16G物理內存、OS爲64位CentOS5.四、Resin做爲Web服務器。
(2)說明:整個服務器暫時沒有部署別的應用,全部硬件資源均可以提供給訪問量並不算太大的網站使用。管理員爲了儘可能利用硬件資源選用了64位的JDK1.5,並經過-Xms和-Xms參數將java堆固定在12GB。
(3)問題:使用一段時間後發現使用效果不理想,網站常常不按期出現長時間沒有響應的現象。
(4)排查:監控服務器運行情況後發現網站沒有響應是因爲GC停頓致使的,虛擬機運行在Server模式中,默認使用吞吐量優先收集器,回收12GB的堆,一次Full GC的停頓時間高達15秒。而且因爲程序設計的關係,訪問文檔時要把文檔從磁盤提取到內存中,致使內存中出現不少由文檔序列化產生的大對象,這些大對象不少都進入了老年代,沒有在Minor GC中清理掉。這種狀況下即便有12GB的堆,內存也很快被消耗殆盡,由此致使每隔十幾分鍾出現十幾秒的停頓。
(5)分析:先不延伸討論程序代碼的問題,程序部署上主要問題顯然是過大的堆內存進行回收時帶來的長時間停頓。硬件升級前使用32位系統1.5GB的堆,用戶只感覺到訪問網站比較緩慢,但不會發生十分明顯的停頓,所以才考慮升級硬件來提高程序效能,若是從新縮小給java堆的內存,那麼硬件上的投資就浪費了。
二、高性能硬件上部署程序的方式
目前主要有兩種方式:
(1)經過64位JDK來使用大內存
(2)使用若干個32位虛擬機創建邏輯集羣來利用硬件資源
三、經過64位JDK來使用大內存
對於用戶交互性強、對停頓時間敏感的系統,能夠給java虛擬機分配超過堆的前提是有把握把應用程序的Full GC頻率控制得足夠低,至少要低到不會影響用戶使用,譬如十幾個小時乃至一天才出現一次Full GC,這樣能夠經過在深夜執行定時任務的方式出發Full GC甚至自動重啓服務器來將內存可用空間保持在一個穩定的水平。
控制Full GC頻率的關鍵是看應用中絕對多數對象可否符合「朝生夕滅」的原則,即大多數對象的生存時間不該太長,尤爲是不能產生批量的、長生存時間的大對象,這樣才能保證老年代空間的穩定。
在大多數網站造成的應用裏,主要對象的生存週期都應該是請求級或頁面級的,會話級和全局級的長生命對象相對減小。只要代碼寫的合理,應當都能實如今超大堆中正常使用而沒有Full GC,這樣的話,使用超大堆內存時,網站響應的速度才比較有保證。除此以外,若是讀者計劃使用64位JDK來管理大內存,還須要考慮下面面臨的問題:
(1)內存回收致使的長時間停頓
(2)現階段,64位JDK的性能測試結果廣泛低於32位JDK
(3)須要保證程序足夠穩定,由於這種應用要是產生堆溢出幾乎沒法產生堆轉儲快照(由於要產生十幾GB乃至更大的dump文件),哪怕產生了快照也幾乎沒法進行解析。
(4)相同的程序在64位JDK中消耗的內存通常比32位JDK大,這是因爲指針膨脹及數據型對其補白等因素致使的。
四、使用若干個32位虛擬機創建邏輯集羣來利用硬件資源
具體作法是在一臺物理機器上啓動多個應用服務器進程,給每一個服務器進行分配不一樣的端口,而後再前端搭建一個負載均衡器,以反向代理的方式來分配訪問請求。讀者不須要太在乎均衡器轉發所消耗的性能,即便使用64位JDK,許多應用也不止有一臺服務器,所以許多應用中前段的均衡器老是要存在的。
考慮到一臺物理機上創建邏輯集羣的目的僅僅是儘量的利用硬件資源,並不須要關心狀態保留、熱轉移之類的高可用性需求,也不須要保證每一個虛擬機進程有絕對準確的均衡負載,所以使用無session複製的親和式集羣是一個至關不錯的選擇。咱們僅僅須要保證集羣具備親和性,也就是均衡器按必定的規則算法將一個固定的用戶請求永遠分配到固定的一個集羣節點進行處理便可,這樣程序開發階段就基本不用爲集羣環境作什麼特別的考慮。
五、使用邏輯集羣的方式部署程序,可能會遇到下面的一些問題
(1)儘可能避免節點競爭全局資源,最典型的是磁盤競爭,各個節點若是同時訪問某個磁盤文件的話(尤爲是併發讀寫操做容易出現問題),很容易致使IO異常。
(2)很難最高效率的利用某些資源池,如鏈接池,通常都是在各個節點創建本身獨立的鏈接池,這樣有可能致使一些節點池滿了而另一些節點仍有較多空餘。儘管可使用集中式的JNDI,但這有必定的複雜性且可能帶來額外的性能代價。
(3)各個節點仍然不可避免的受到32位的內存限制,在32位Windows平臺中每一個進程只能使用2GB的內存,考慮到堆之外的內存開銷,堆通常最多隻能開到1.5GB。在某些Linux,Unix系統中,能夠提高到3GB乃至接近4GB的內存,但32位中仍然受最高4GB(2^32)內存的限制。
(4)大量使用本地緩存(如大量使用hashmap所謂K/V緩存)的應用,在邏輯集羣中形成較大的內存浪費,由於每一個邏輯節點上都有一份緩存,這時能夠考慮把本地緩存改爲集中式緩存。
六、小結
介紹完這兩種部署方式,再從新回到這個案例中,最後的部署方案調整爲創建5個32位JDK的邏輯集羣,每一個進程按2GB內存計算,佔用了10GB的內存。另外創建一個Apache服務做爲前端均衡器代理訪問門戶。考慮到用戶對響應要求較低,所以改成CMS收集器進行垃圾回收。部署完成後,服務再沒有出現長時間停頓現象,速度比硬件升級前有較多提高。
3、eclipse運行速度調優
4、總結
調優確實是一個「苦力」活,但對人員素質要求比較高,好像上述所說的經歷,就是由於經驗不足帶致使這麼長的調優時間。固然,陽光總在風雨後,熬過去了,確實進步很多,寫代碼的大局觀也提高很多。因此調優這事,是面試的一大好題,最能反映我的能力。
鳴謝:特別感謝做者周志明提供的技術支持!