1. 類加載機制分析
Java類(.java) --> 字節(jié)碼文件(.class)--> 字節(jié)碼文件需要被加載到JVM內(nèi)存中(這個過程就是一個類加載的過程)
類加載器(ClassLoader,說白了也是一個類),jvm啟動的時候先把類加載器讀取到內(nèi)存中去。
1.1 JVM類加載機制
jvm類加載機制中有一個非常重要的角色叫做類加載器(ClassLoader),類加載器有自己的體系。JVM內(nèi)置了幾種類加載器:引導(dǎo)類加載器,擴展類加載器,系統(tǒng)類加載器,他們之間形成父子關(guān)系,通過Parent屬性來定義這種關(guān)系,最終形成樹形結(jié)構(gòu)

| 類加載器 | 作用 |
|---|---|
| 引導(dǎo)類加載器<br />BootstrapClassLoader | c++編寫,加載java核心庫java.*,比如rt.jar中的類。構(gòu)造ExtClassLoader和APPClassLoader |
| 擴展類加載器<br />ExtClassLoader | java編寫,加載擴展庫JAVA_HOME/lib/ext目錄下的jar中的類,如classpath中的jre,javax.*或者java.ext.dir指定位置中的類 |
| 系統(tǒng)類加載器<br />SystemClassLoader/AppClassLoader | 默認的類加載器,搜索環(huán)境變量classpath中指明的路徑 |
另外:用戶可以自定義類加載器(Java編寫,用戶自定義的類加載器,可加載指定路徑的class文件)
當JVM運行過程中,用戶自定義了類加載器去加載某些類時,會按照以下的步驟(父類委托機制)
1)用戶自己的類加載器,把加載請求傳給父類加載器,父加載器再傳給其父加載器,一直到加載器樹的頂層
2)最頂層的類加載器首先針對其特定的位置加載,如果加載不到就轉(zhuǎn)交給子類
3)如果一直到底層的類加載器都沒有加載到,那么就會拋出異常ClassNotFoundException
因此,按照這個過程可以想到,如果同樣在 classpath 指定的目錄中和自己工作的目錄中存放相同的class,會優(yōu)先加載 classpath 目錄中的文件
1.2 雙親委派機制
1.2.1 什么是雙親委派機制
當某個類加載器需要加載某個 .class 文件時,它首先把這個任務(wù)委托給他的上級類加載器,遞歸這個操作,如果上級的類加載器沒有加載,自己才會去加載這個類。
1.2.2 雙親委派機制的作用
防止加載同一個 .class 文件。通過委托機制向上面問一問,加載過來,就不用再加載一遍。保證數(shù)據(jù)安全
-
保證核心 .class 不能被篡改。通過委托方式,不會去篡改核心 .class,即使篡改也不會去加載,即使加載也不會是同一個 .class 對象了。不同的加載器加載同一個 .class 也不是同一個 .class 對象。這樣保證了class 執(zhí)行安全(如果子類加載器先加載,那么我們可以寫一些與java.lang包中基礎(chǔ)類同名的類,然后再定義一個子類加載器,這樣整個應(yīng)用使用的基礎(chǔ)類就都變成我們自己定義的類了)。
Object類 -----> 自定義類加載器(會出現(xiàn)問題,那么真正的Object類就可能被篡改了)
1.3 Tomcat 的類加載機制
Tomcat 的類加載機制相對于 JVM 的類加載機制做了一些改變。
沒有嚴格的遵從雙親委派機制,也可以說打破了雙親委派機制。
比如:有一個tomcat,webapps下部署了兩個應(yīng)用
app1/lib/a-1.0.jar com.app.Abc
app2/lib/a-2.0.jar com.app.Abc
不同的版本中Abc類的內(nèi)容是不同的,代碼是不一樣的。如果完全遵從雙親委派機制,兩個應(yīng)用下Abc只會被加載一個,這樣就是有問題的。

引導(dǎo)類加載器 和 擴展類加載器 作用不變
系統(tǒng)類加載器正常情況下加載的是 CLASSPATH 下的類,但是 Tomcat 的啟動腳本并未使用該變量,而是加載 Tomcat 啟動的類,比如 bootstrap.jar,通常在 catalina.bat 或者 catalina.sh 中指定,位于 CATALINA_HOME/bin 下
Common 通用類加載器加載 Tomcat 使用以及應(yīng)用通用的一些類,位于 CATALINA_PATH/lib 下,比如servlet-api.jar
Catalina ClassLoader 用于加載服務(wù)器內(nèi)部可見類,這些類應(yīng)用程序不能訪問
Shared ClassLoader 用于加載應(yīng)用程序共享類,這些類服務(wù)器不會依賴
-
WebApp ClassLoader,每個應(yīng)用程序都還有一個獨一無二的 WebApp ClassLoader , 他用來加載本應(yīng)用程序 /WEB-INF/classes 和 /WEB-INF/lib 下的類。
tomcat 8.5 默認改變了嚴格的雙親委派機制
- 首先從 Bootstrap ClassLoader 加載指定的類
- 如果未加載到,則從 /WEB-INF/classes 加載
- 如果未加載到,則從 /WEB-INF/lib 加載
- 如果未加載到,則依次從 System、Common、Shared加載(在最后一步,遵從雙親委派機制)
2. Tomcat 優(yōu)化性能策略
系統(tǒng)性能的衡量指標,主要是響應(yīng)時間和吞吐量。
響應(yīng)時間:執(zhí)行某個操作的耗時。
吞吐量:系統(tǒng)在給定時間內(nèi)能夠支持的事務(wù)數(shù)量,單位為TPS(Transactions PerSencod的縮寫,也就是事務(wù)數(shù)/秒,一個事務(wù)是指一個客戶機向服務(wù)器發(fā)送請求然后服務(wù)器做出反應(yīng)的過程)。
Tomcat 優(yōu)化從兩方面進行
1)JVM虛擬機優(yōu)化(優(yōu)化內(nèi)存模型)
2)Tomcat 自身配置優(yōu)化(比如是否使用共享線程池?IO模型?)
優(yōu)化原則:
優(yōu)化是一個過程,沒有明確的參數(shù)值供使用,必須根據(jù)自己的真實生產(chǎn)環(huán)境來進行調(diào)整。
2.1 虛擬機運行優(yōu)化
Java 虛擬機的運行優(yōu)化主要分為內(nèi)存分配和垃圾回收策略的優(yōu)化:
- 內(nèi)存直接影響服務(wù)的運行效率和吞吐量
- 垃圾回收機制會不同程度的導(dǎo)致程序運行中斷(垃圾回收策略不同,垃圾回收次數(shù)和回收效率都是不同的)
2.1.1 Java 虛擬機內(nèi)存相關(guān)參數(shù)
| 參數(shù) | 參數(shù)作用 | 優(yōu)化建議 |
|---|---|---|
| -server | 啟動server,以服務(wù)端模式運行 | 服務(wù)端模式建議開啟 |
| -Xms | 最小堆內(nèi)存 | 建議與-Xmx設(shè)置相同 |
| -Xmx | 最大堆內(nèi)存 | 建議設(shè)置為可用內(nèi)存的80% |
| -XX:MetaspaceSize | 元空間初始值 | |
| -XX:MaxMetaspaceSize | 元空間最大內(nèi)存 | 默認無線 |
| -XX:NewRatio | 年輕代和老年代大小比值,取值為整數(shù),默認2 | 不需要修改 |
| -XX:SurvivorRatio | Eden區(qū)和Survivor區(qū)大小的比值,取值為整數(shù),默認8 | 不需要修改 |
java內(nèi)存模型

參數(shù)調(diào)整示例
JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
調(diào)整后查看可以使用JDK提供的內(nèi)存映射工具查看Tomcat中JVM的內(nèi)存配置
jhsdb jmap --heap --pid xxxx
2.1.2 垃圾回收(GC)策略
垃圾回收性能指標
- 吞吐量:工作時間(排除GC時間)占總時間的百分比,工作時間并不僅是程序的運行時間,還包括內(nèi)存分配時間
- 暫停時間:由垃圾回收導(dǎo)致的應(yīng)用程序停止響應(yīng)次數(shù)/時間
垃圾收集器
-
串行收集器(Serial Collector)
單線程執(zhí)行所有的垃圾回收工作,適用于單核CPU服務(wù)器
工作進程 ----- |(暫停) ---- (單線程)垃圾回收線程進行垃圾收集 | ---工作進程繼續(xù)
-
并行收集器(Parallel Collector)
又稱為吞吐量收集器(關(guān)注吞吐量),以并行的方式執(zhí)行年輕代的垃圾回收,該方式可以顯著降低垃圾回收開銷(指多條垃圾收集器線程并行工作,但此時用戶線程任然處于等待狀態(tài))。適用于多處理或多線程硬件上運行的數(shù)據(jù)量較大的應(yīng)用
工作進程 ----- |(暫停) ---- (多線程)垃圾回收線程進行垃圾收集 | ---工作進程繼續(xù)
-
并發(fā)收集器(Concurrent Collector)
以并發(fā)的方式執(zhí)行大部分垃圾回收工作,以縮短垃圾回收的暫停時間。適用于那些響應(yīng)時間優(yōu)于吞吐量的應(yīng)用,因為該收集器雖然最小化了暫停時間(指用戶線程與垃圾收集器線程同時執(zhí)行,但不一定是并行,可能會交替進行),但是會降低應(yīng)用的性能。
-
CMS收集器(Concurrent Mark Sweep Collector)
并發(fā)標記清除收集器,適用于那些更愿意縮短垃圾回收暫停時間并且負擔的起與垃圾回收共享處理器資源的應(yīng)用
-
G1收集器(Garbage-First Grabage Collector)
適用于大容量內(nèi)存的多核服務(wù)器,可以滿足垃圾回收暫停時間目標的同時,以最大可能性實現(xiàn)高吞吐(JDK1.7之后)
垃圾回收參數(shù)
| 參數(shù) | 描述 |
|---|---|
| -XX:+UseSerialGC | 啟用串行收集器 |
| -XX:+UseParallelGC | 啟用并行收集器,配置了該選項,那么 -XX:+UseParallelOldGC 默認啟用 |
| -XX:+UseParNewGC | 年輕代請用并行收集器,如果設(shè)置了 -XX:+UseConcMarkSweepGC 選項,自動啟動 |
| -XX:ParallelGCThreads | 年輕代以及老年代垃圾回收使用的線程數(shù)。默認值依賴于JVM使用的CPU個數(shù) |
| -XX:+UseConcMarkSweepGC(CMS) | 對應(yīng)老年代,啟用CMS垃圾收集器。當并行收集器無法滿足應(yīng)用的延遲需求時,推薦使用CMS或G1收集器。啟用該選項后,-XX:+UseParallelGC 自動啟用 |
| -XX:+UseG1GC | 啟用G1收集器,G1是服務(wù)器類型的收集器,用于多核、大內(nèi)存的機器。它在保持高吞吐量的情況下,高概率的滿足GC暫停時間的目標 |
在 bin/catalina.sh 的腳步中,追加配置:
JAVA_OPTS="-XX:+UseConcMarkSweepGC"
2.2 Tomcat 配置優(yōu)化
Tomcat 自身相關(guān)優(yōu)化
2.2.1 調(diào)整tomcat線程池

2.2.2 調(diào)整tomcat的連接器

2.2.3 禁用AJP連接器

2.2.4 調(diào)整IO模型

2.2.5 動靜分離
