- 高性能Java架構:核心原理與案例實戰
- 張方興編著
- 2764字
- 2021-10-15 18:26:06
1.4 算法、GC與診斷工具
1.4.1 算法
“算法”(algorithm)一詞得名于波斯數學家花拉子密。公元9世紀,這位數學家寫過一本書,討論用紙筆解決數學問題的技巧。書名為al-Jabr wa’l-Muqabala,其中,“al-jabr”就是后來“algebra”(代數)這個詞的前身。不過,最早的數學算法早于花拉子密。在巴格達附近出土的4000年前的蘇美爾人的泥板文獻上,就刻有一幅長除法示意圖。
但是,算法不僅限于數學。當按照食譜介紹烤面包時,食譜上的所有步驟就是一個算法。當按照圖樣編織毛衣時,這份圖樣就是一個算法。使用鹿角的末端連續精確地敲打,使石器形成鋒利的刃的過程(這是制作精密石器的一個關鍵步驟),也遵循著一個算法。從石器時代開始,算法就已經是人類生活的一部分了。
——《算法之美》[美]布萊恩·克里斯汀;[美]湯姆·格里菲思 著
在Java編程中,大部分程序員所接觸的算法與數學并不相關,一些數學題在工作上很難體現出意義,但并不是說算法就毫無意義了,相反,算法在Java程序優化的過程中有著舉足輕重的地位。大多數時候,算法與GC和業務邏輯息息相關。例如,優化循環次數、優化業務邏輯、減少內存開銷、減少從數據源處提取的數據量、每次GC都刪除更多的垃圾、把同步線程換為異步線程、把多次線程開銷優化為線程池等,以上優化皆屬于算法。
在Java編程中,算法上的經驗大多來自工作中的經驗。例如,雖然某個接口增加了緩存,但是返回速度仍然很慢,此時就需要根據Arthas之類的診斷工具配合GC相關的內容,不斷地優化自身的代碼。如果無法優化算法,則先優化業務需求,再優化算法。
1.4.2 GC
在Java中,各版本所涵蓋的GC(垃圾回收器)都不同,最著名的有三款,分別為CMS、G1和ZGC。
在JDK 1.3之前,垃圾回收是單線程回收的,并且會stop the world(下文簡稱為STW)。也就是說,在垃圾回收時,會暫停所有用戶線程。由于其運行方式是單線程的,所以適合Client模式的應用和單CPU環境。串行的GC有兩種,即Serial和SerialOld,一般會搭配使用。在年輕代使用Serial復制算法,在年老代使用Serial Old標記整理算法。客戶端應用或者命令行程序可以通過-XX:+UseSerialGC開啟上述回收模式。
G1(Garbage First)先結合OpenJDK源碼分析重要算法(如SATB)和重要存儲結構(如CSet、RSet、TLAB、PLAB、Card Table等),再梳理G1 GC的YoungGC和MixedGC的收集過程。GC的主要回收區域是年輕代、年老代和持久代。在JDK8之后,持久代被替換成元空間(Metaspace)。元空間會在普通的堆區上進行分配。為了提高垃圾回收效率,G1通常采用分代回收的方式,即對不同的回收區域使用不同的GC。在系統正常運行時,Full GC會觸發整個堆的掃描和回收(這在年輕代中是比較頻繁的)。在G1中,最好的優化狀態是不斷地調整分區空間,避免進行Full GC,以便大幅提高吞吐量。
CMS(Concurrent Mark Sweep)基于標記—刪除算法,主要用于年老代,所以其關注點在于減少因垃圾回收導致的STW時間。對于重視服務響應速度的應用可以使用CMS。CMS是并發運行的,即垃圾回收線程可以和用戶線程同時運行。
ZGC與G1和CMS的設計不同,ZGC是革命性的垃圾回收器。在JDK9之后,默認的垃圾回收器是G1,自JDK11之后,ZGC成為新的垃圾回收器,它取消了年輕代和年老代的設計模式,ZGC只把Page作為內存存儲,支持最大4TB級堆內存,最長停頓時間為10ms、吞吐量最大不超過15%。ZGC只對內存數據進行標記,而不會單獨放置在一個空間中,不會出現Full GC的情況。
1.4.3 jvmtop
jvmtop是一個輕量級的控制臺程序,可用來監控機器上運行的所有Java虛擬機,它顯示了很多JVM內部信息,如內存等,使用命令如下所示:

1.4.4 jstat
jstat可以查看堆內存各部分的使用量,以及加載類的數量,命令格式如下:

下面通過jstat查看年老代信息,即查看pid為123456的Java程序的每秒GC信息,如下所示:

在用jstat查看年老代信息后,部分顯示內容說明如下所示:
? S0C:年輕代中第一個survivor(幸存者區)的容量(字節)。
? S1C:年輕代中第二個幸存者區的容量(字節)。
? S0U:年輕代中第一個幸存者區目前已使用空間(字節)。
? S1U:年輕代中第二個幸存者區目前已使用空間(字節)。
? EC:年輕代中Eden(伊甸園)的容量(字節)。
? EU:年輕代中伊甸園目前已使用空間(字節)。
? OC:年老代的容量(字節)。
? OU:年老代目前已使用空間(字節)。
? PC:持久代的容量(字節)。
? PU:持久代目前已使用空間(字節)。
? YGC:從應用程序啟動到采樣時年輕代中的垃圾回收次數。
? YGCT:從應用程序啟動到采樣時年輕代的垃圾回收所用時間(s)。
? FGC:從應用程序啟動到采樣時年老代的(Full GC)垃圾回收次數。
? FGCT:從應用程序啟動到采樣時年老代的(Full GC)垃圾回收所用時間(s)。
? GCT:從應用程序啟動到采樣時垃圾回收所用的總時間(s)。
? NGCMN:年輕代的初始化大小(字節)。
? NGCMX:年輕代的最大容量(字節)。
? NGC:年輕代的當前容量(字節)。
? OGCMN:年老代的初始化大小(字節)。
? OGCMX:年老代的最大容量(字節)。
? OGC:年老代當前新生成的容量(字節)。
? PGCMN:持久代的初始化大小(字節)。
? PGCMX:持久代的最大容量(字節)。
? PGC:持久代當前新生成的容量(字節)。
? S0:年輕代中第一個幸存者區已使用的容量占當前容量的百分比。
? S1:年輕代中第二個幸存者區已使用的容量占當前容量的百分比。
? E:年輕代中伊甸園已使用的容量占當前容量的百分比。
? O:年老代中已使用的容量占當前容量的百分比。
? P:持久代中已使用的容量占當前容量的百分比。
? S0CMX:年輕代中第一個幸存者區的最大容量(字節)。
? S1CMX:年輕代中第二個幸存者區的最大容量(字節)。
? ECMX:年輕代中伊甸園的最大容量(字節)。
? DSS:當前需要幸存者區的容量(字節)(伊甸園已滿)。
? TT:持有次數限制。
? MTT:最大持有次數限制。
1.4.5 Arthas
Arthas是阿里巴巴開源的Java診斷工具,它集成了jvmtop與jstat等絕大部分Java診斷工具,并進行了創新。當遇到以下類似問題而束手無策時,Arthas可以快速解決。
? 這個類是從哪個jar包加載的?為什么會報各種類相關的異常?
? 我改的代碼為什么沒有執行到?是沒有提交,還是分支搞錯了?
? 遇到問題無法在線上debug,難道只能通過加日志再重新發布嗎?
? 線上遇到某個用戶的數據處理有問題,但線上無法debug,而線下無法重現?
? 是否可以通過全局視角查看系統的運行狀況?
? 是否可以監控JVM的實時運行狀態?
? 如何快速定位應用的熱點,生成火焰圖?
Arthas支持JDK 6以上版本,支持Linux、Mac和Windows操作系統。它采用命令行交互模式,同時提供了豐富的Tab自動補全功能,可以方便地對問題進行定位和診斷。
Arthas包含大量命令,此處僅介紹部分與性能有關的命令。例如,通過sysprop命令可以查看當前Java程序中的所有System Properties信息,結果如圖1-1所示。
通過thread命令可以查看當前Java程序中的所有線程信息,結果如圖1-2所示。
通過trace命令可以查看方法的調用耗時,結果如圖1-3所示。

圖1-1

圖1-2

圖1-3
通過monitor命令可以監控方法的實行進度及耗時,結果如圖1-4所示。

圖1-4
除此之外,Arthas還可以查詢最近5秒CPU使用率最高的線程、獲取函數調用棧、反編譯并直接在jar包處修改正在執行的代碼、跟蹤所有的Filter函數、動態修改當前運行程序的Logger日志等級、獲取當前運行程序的static變量數值等,是Java調優中一個功能強大的工具包。
- 微服務設計(第2版)
- MySQL數據庫應用與管理 第2版
- Building a RESTful Web Service with Spring
- 單片機C語言程序設計實訓100例:基于STC8051+Proteus仿真與實戰
- oreilly精品圖書:軟件開發者路線圖叢書(共8冊)
- Hadoop+Spark大數據分析實戰
- Mastering ServiceNow(Second Edition)
- Cocos2d-x學習筆記:完全掌握Lua API與游戲項目開發 (未來書庫)
- Apache Kafka Quick Start Guide
- Service Mesh實戰:基于Linkerd和Kubernetes的微服務實踐
- RESTful Java Web Services(Second Edition)
- Frank Kane's Taming Big Data with Apache Spark and Python
- 后臺開發:核心技術與應用實踐
- 快樂編程:青少年思維訓練
- Node.js 6.x Blueprints