專題?標(biāo)
本系列專題的目標(biāo)是希望可以幫助讀者們系統(tǒng)和全訪問掌握應(yīng)?系統(tǒng)調(diào)優(yōu)的思路與方案以及相關(guān)的調(diào)優(yōu)工具的使用忌傻,雖然未必會(huì)覆蓋目前的所有的問題場(chǎng)景,但是還是提供了較為豐富的案例和調(diào)優(yōu)理論份乒,會(huì)幫助大家打開思維去?撐系統(tǒng)服務(wù)體系優(yōu)化能力盐茎。
適合人員
Java相關(guān)的開發(fā)人員袭蝗、系統(tǒng)架構(gòu)師、數(shù)據(jù)庫DB人員以及運(yùn)維人員等斋攀。
什么是調(diào)優(yōu)
調(diào)優(yōu)手段就是讓計(jì)算機(jī)的硬件或軟件在正常地?作基礎(chǔ)上,非常出色的發(fā)揮其應(yīng)有的性能梧田,并且將所承擔(dān)的負(fù)擔(dān)降低到最低的技術(shù)手段淳蔼。在Java應(yīng)用服務(wù)體系中有大致可以分為5個(gè)維度的調(diào)優(yōu)方向。
調(diào)優(yōu)技術(shù)的五個(gè)維度
- 應(yīng)??身的調(diào)優(yōu)
- 運(yùn)?環(huán)境的調(diào)優(yōu)(JVM的調(diào)優(yōu))
- 存儲(chǔ)上的調(diào)優(yōu)(數(shù)據(jù)庫的調(diào)優(yōu))
- 操作系統(tǒng)的調(diào)優(yōu)
- 架構(gòu)上的調(diào)優(yōu)
如下圖所示裁眯。
一般從上到下系統(tǒng)優(yōu)化的層面成本越來越高鹉梨,而從下到上系統(tǒng)優(yōu)化層面的成本越來月底,而且難度也適當(dāng)下降未状,建議自下而上的去進(jìn)行調(diào)優(yōu)規(guī)劃俯画。
調(diào)優(yōu)技術(shù)的四條準(zhǔn)則
借助監(jiān)控預(yù)防問題、發(fā)現(xiàn)問題司草,監(jiān)控 + 告警
采用監(jiān)控和預(yù)防的手段去實(shí)現(xiàn)提前發(fā)現(xiàn)問題:zabbix艰垂、promethus等等
借助?具定位問題
問題排查工具使用機(jī)制
定期復(fù)盤,防?同類問題再現(xiàn)
定期進(jìn)行排查和復(fù)盤相關(guān)的代碼問題埋虹,加深我們對(duì)問題的印象以及防止問題再次發(fā)生
定好規(guī)范猜憎,?定程度上規(guī)避問題
制定標(biāo)準(zhǔn)規(guī)范,約束問題的發(fā)生搔课。
調(diào)優(yōu)的原則
有問題胰柑,解決問題。not broken, don't fix.
應(yīng)?調(diào)優(yōu)
應(yīng)?調(diào)優(yōu)-?具篇
?具旨在幫助我們快速找到應(yīng)?的性能瓶頸爬泥。
?志分析?具?較與分析
ELK柬讨、GrayLog、SLSLog...
ELK搭建與使?
現(xiàn)場(chǎng)演示
調(diào)?鏈跟蹤?具與對(duì)?
Skywalking袍啡、Sleuth + Zipkin踩官、Jaeger...
Skywalking快速發(fā)現(xiàn)性能瓶頸
應(yīng)?調(diào)優(yōu)常?技巧-池化技術(shù)-對(duì)象池
通過復(fù)?對(duì)象,減少對(duì)象創(chuàng)建境输、垃圾回收的開銷
適?場(chǎng)景
維護(hù)?些很?蔗牡、創(chuàng)建很慢的對(duì)象,提升性能
缺點(diǎn):有學(xué)習(xí)成本嗅剖、增加了代碼的復(fù)雜度
對(duì)象池框架
Apache Commons-Pool2
Commons-Pool2詳解
兩?類對(duì)象池:ObjectPool & KeyedObjectPool
ObjectPool
實(shí)現(xiàn)類如下辩越,其中,最重要信粮、功能最強(qiáng)黔攒、使?最?泛的GenericObjectPool,這個(gè)對(duì)象池?常的強(qiáng)?,它?較的通?亏钩,?且封裝得也?常完備莲绰。
- BaseObjectPool:抽象類,?來擴(kuò)展??的對(duì)象池
- ErodingObjectPool:“腐蝕”對(duì)象池姑丑,代理?個(gè)對(duì)象池蛤签,并基于factor參數(shù),為其添加“腐蝕”?為栅哀。歸還的對(duì)象被腐蝕后震肮,將會(huì)丟棄,?不是添加到空閑容量中留拾。
- GenericObjectPool:?個(gè)可配置的通?對(duì)象池實(shí)現(xiàn)戳晌。
- ProxiedObjectPool:代理?個(gè)其他的對(duì)象池,并基于動(dòng)態(tài)代理(?持JDK代理和CGLib代理)痴柔,返回?個(gè)代理后的對(duì)象沦偎。該對(duì)象池主要?來增強(qiáng)對(duì)池化對(duì)象的控制,?如防?在歸還該對(duì)象后咳蔚,還繼續(xù)使?該對(duì)象等豪嚎。
- SoftReferenceObjectPool:基于軟引?的對(duì)象池
- SynchronizedObjectPool:代理?個(gè)其他對(duì)象池,并為其提供線程安全的能?谈火。
核?API如下
- borrowObject() 從對(duì)象池中借對(duì)象
- returnObject() 將對(duì)象歸還到對(duì)象池
- invalidateObject() 失效?個(gè)對(duì)象
- addObject() 增加?個(gè)空閑對(duì)象侈询,該?法適?于使?空閑對(duì)象預(yù)加載對(duì)象池
- clear() 清空空閑的所有對(duì)象,并釋放相關(guān)資源
- close() 關(guān)閉對(duì)象池糯耍,并釋放相關(guān)資源
- getNumIdle() 獲得空閑的對(duì)象數(shù)量
- getNumActive() 獲得被借出對(duì)象數(shù)量
KeyedObjectPool
這種對(duì)象池和ObjectPool的區(qū)別在于扔字,它是通過key找對(duì)象的,從設(shè)計(jì)上來看和ObjectPool沒什么區(qū)別温技。實(shí)現(xiàn)類如下革为,使?最?的是GenericKeyedObjectPool。
- ErodingKeyedObjectPool 類似ErodingObjectPool
- GenericKeyedObjectPool 類似GenericObjectPool
- ProxiedKeyedObjectPool 類似ProxiedObjectPool
- SynchronizedKeyedObjectPool 類似SynchronizedObjectPool
使?
new GenericObjectPool(PooledObjectFactory<T> factory)
new GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig<T> config)
new GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig<T> config, AbandonedConfig abando
nedConfig)
最重要的參數(shù)是PooledObjectFactory舵鳞,?般來說篷角,??是需要我們??根據(jù)業(yè)務(wù)需求去實(shí)現(xiàn)的。它是?來創(chuàng)建對(duì)象的系任,這其實(shí)就是設(shè)計(jì)模式??的??模式。
?前PooledObjectFactory有兩個(gè)實(shí)現(xiàn)類虐块。
- BasePooledObjectFactory:抽象類俩滥,?于擴(kuò)展??的PooledObjectFactory
- PoolUtils.SynchronizedPooledObjectFactory:內(nèi)部類,代理?個(gè)其他的PooledObjectFactory贺奠,實(shí)現(xiàn)線程同步霜旧,? PoolUtils.synchronizedPooledFactory() 創(chuàng)建
Factory核??法:
- makeObject 創(chuàng)建?個(gè)對(duì)象實(shí)例,并將其包裝成?個(gè)PooledObject
- destroyObject 銷毀對(duì)象
- validateObject 校驗(yàn)對(duì)象,確保對(duì)象池返回的對(duì)象是OK的
- activateObject 重新初始化對(duì)象
- passivateObject 取消初始化對(duì)象挂据。GenericObjectPool的addIdleObject以清、returnObject、evict調(diào)?該?法崎逃。
Commons-Pool2總體分析
- ObjectPool:對(duì)象池掷倔,最核?:GenericObjectPool、 GenericKeyedObjectPool个绍。
- Factory:創(chuàng)建&管理PooledObject勒葱,?般要??擴(kuò)展
- PooledObject:包裝原有的對(duì)象,從?讓對(duì)象池管理巴柿,?般?DefaultPooledObject即可
Factory示例
class MyPooledObjectFactory implements PooledObjectFactory<Model> {
public static final Logger LOGGER = LoggerFactory.getLogger(MyPooledObjectFactory.class);
@Override
public PooledObject<Model> makeObject() throws Exception {
DefaultPooledObject<Model> object = new DefaultPooledObject<>(new Model(1, "S"));
LOGGER.info("makeObject..state = {}", object.getState());
return object;
}
@Override
public void destroyObject(PooledObject p) throws Exception{
LOGGER.info("destroyObject..state = {}", object.getState());
}
@Override
public boolean validateObject(PooledObject p) {
LOGGER.info("validateObject..state = {}", object.getState());
return true;
}
@Override
public void activateObject(PooledObject p) throws Exception{
LOGGER.info("activateObject..state = {}", p.getState());
}
@Override
public void passivateObject(PooledObject p) {
LOGGER.info("passivateObject..state = {}", object.getState());
return true;
}
所有操作面向的都是PooledObject這個(gè)參數(shù)凛虽,makeObject返回的是PooledObject,其他API為什么操作的也是 PooledObject广恢,?不是直接操作我們創(chuàng)建的對(duì)象呢凯旋?
這其實(shí)也是commons-pool設(shè)計(jì)巧妙之處。Pooledobject可以對(duì)原始對(duì)象進(jìn)?包裝钉迷,從?被對(duì)象池管理至非。?前 pooledobject有兩個(gè)實(shí)現(xiàn)類:
- DefaultPooledObject:包裝原始對(duì)象,實(shí)現(xiàn)監(jiān)控(例如創(chuàng)建時(shí)間篷牌、使?時(shí)間等)睡蟋、狀態(tài)跟蹤等
- PooledSoftReference:封裝了DefaultPooledObject,?來和SoftReferenceObjectPool配合使?枷颊。
DefaultPooledObject定義了對(duì)象的若?種狀態(tài)
- IDLE 對(duì)象在隊(duì)列中戳杀,并空閑。
- ALLOCATED 使?中(即出借中)
- EVICTION 對(duì)象當(dāng)在隊(duì)列中夭苗,正在進(jìn)?驅(qū)逐測(cè)試
- EVICTION_RETURN_TO_HEAD 對(duì)象驅(qū)逐測(cè)試通過后信卡,放回到隊(duì)列頭部
- VALIDATION 對(duì)象當(dāng)前在隊(duì)列中,空閑校驗(yàn)中
- VALIDATION_PREALLOCATED 對(duì)象當(dāng)前不在隊(duì)列中题造,出借前校驗(yàn)中 VALIDATION_RETURN_TO_HEAD 對(duì)象當(dāng)前不在隊(duì)列中傍菇,校驗(yàn)通過后放回頭部 INVALID 對(duì)象失效,驅(qū)逐測(cè)試失敗界赔、校驗(yàn)失敗丢习、對(duì)象銷毀,都會(huì)將對(duì)象置為 INVALID淮悼。
- ABANDONED 放逐中咐低,如果對(duì)象上次使?時(shí)間超過removeAbandonedTimeout的配置,則將其標(biāo)記為ABANDONED袜腥。標(biāo)記為ABANDONED的對(duì)象即將變成 INVALID见擦。
- RETURNING 對(duì)象歸還池中。
JVM調(diào)優(yōu)
本系列專題將針對(duì)于Oracle Java HotSpot虛擬機(jī)為為開發(fā)者們提供不同的Java Heap內(nèi)存空間的較為深入的分析介紹。對(duì)于任何接觸的開發(fā)者都是非常重要的理論依據(jù)鲤屡。頻繁遇到的內(nèi)存問題损痰,提供生產(chǎn)環(huán)境的優(yōu)化調(diào)整。那么適當(dāng)?shù)膶?shí)戰(zhàn)層級(jí)的Java虛擬機(jī)的內(nèi)存空間分析能力是至關(guān)重要的酒来。
前提概述
- Java虛擬機(jī)是你的Java程序運(yùn)行的基礎(chǔ)卢未,它為你提供動(dòng)態(tài)的分配內(nèi)存服務(wù)、垃圾收集役首、線程調(diào)度和切換尝丐、IO處理和本機(jī)操作等
- Java堆空間是運(yùn)行時(shí)Java程序的內(nèi)存“容器”,它提供給您的Java應(yīng)用程序所需的適當(dāng)內(nèi)存空間(Java堆衡奥、本機(jī)堆)爹袁,并由JVM本身去管理。
JVM HotSpot內(nèi)存被劃分2類和5空間:
- Heap堆內(nèi)存空間:屬于線程共享區(qū)域矮固,也是我們JVM的內(nèi)存管理范疇的最大的一部分運(yùn)行時(shí)內(nèi)存區(qū)域失息。
- 方法區(qū)(永久代/元空間):屬于線程共享區(qū)域,往往我們會(huì)忽略了這個(gè)區(qū)域的內(nèi)存回收能力档址。
- 本地堆 (C-Heap):本地方法的調(diào)用棧盹兢。
- 虛擬機(jī)棧:Java方法的調(diào)用棧。
Heap堆內(nèi)存空間
JVM的堆空間的變化在<18的版本之內(nèi)守伸,主要有一個(gè)分水嶺绎秒,主要集中在8之前和8之后。
JDK8之前的對(duì)空間
JDK8之前的Heap空間如下圖所示:
JDK8之后的Heap空間如下圖所示:
主要時(shí)針對(duì)于方法區(qū)的實(shí)現(xiàn)機(jī)制:永久代 -> 元空間結(jié)構(gòu)模型尼摹,接下來我們看看元數(shù)據(jù)空間在方法區(qū)中的分布結(jié)構(gòu)模型见芹。
后續(xù)版本中的-元空間和方法去的內(nèi)存才能出分配關(guān)系
可以看到JDK8之后,方法去的實(shí)現(xiàn)有元空間和一部分堆內(nèi)存組成蠢涝。之前主要只有單純的永久代去實(shí)現(xiàn)的玄呛。
常量池
常量池主要有靜態(tài)常量池和運(yùn)行時(shí)常量池組成。
- 類信息
- 類的版本
- 字段描述信息
- 方法描述信息
- 接口和父類等描述信息
- class文件常量池(靜態(tài)常量池)
靜態(tài)常量池和二,也叫class?件常量池徘铝,主要存放:
- 字?量:例如?本字符串、 final修飾的常量惯吕。
- 符號(hào)引?:例如類和接?的全限定名惕它、字段的名稱和描述符、?法的名稱和描述符废登。
運(yùn)?時(shí)常量池
當(dāng)類加載到內(nèi)存中后怠缸,JVM就會(huì)將靜態(tài)常量池中的內(nèi)容存放到運(yùn)?時(shí)的常量池中;運(yùn)?時(shí)常量池??存儲(chǔ)的主要是編譯期間?成的字?量钳宪、符號(hào)引?等等。如下圖對(duì)應(yīng)的字符串常量在字符串常量池中的存儲(chǔ)模式。
字符串常量池
字符串常量池吏颖,也可以理解成運(yùn)?時(shí)常量池分出來的?部分搔体,類加載到內(nèi)存的時(shí)候,字符串半醉,會(huì)存到字符串常量池??疚俱。
對(duì)象和類在內(nèi)存分布
針對(duì)于代碼的執(zhí)行和存儲(chǔ)在JVM的分布,主要集中在椝醵啵空間和堆空間呆奕、方法區(qū)。它們各個(gè)的職能不同衬吆,對(duì)應(yīng)的能力也是不同的梁钾。我們針對(duì)于一段代碼塊進(jìn)行分析和介紹
虛擬機(jī)棧的基本結(jié)構(gòu)模型
代碼在堆棧中的存儲(chǔ)結(jié)構(gòu)信息
內(nèi)存泄漏怎么排查[java內(nèi)存溢出排查]
top 等查看系統(tǒng)內(nèi)存概況
top:顯示所有進(jìn)程運(yùn)行情況,按M鍵按照內(nèi)存大小排序逊抡。
使用格式
top [-] [d] [p] [q] [c] [C] [S] [s] [n]
參數(shù)說明
- d:指定每?jī)纱纹聊恍畔⑺⑿轮g的時(shí)間間隔姆泻,當(dāng)然用戶可以使用s交互命令來改變之。
- p:通過指定監(jiān)控進(jìn)程ID來僅僅監(jiān)控某個(gè)進(jìn)程的狀態(tài)冒嫡。
- q:該選項(xiàng)將使top沒有任何延遲的進(jìn)行刷新拇勃。如果調(diào)用程序有超級(jí)用戶權(quán)限,那么top將以盡可能高的優(yōu)先級(jí)運(yùn)行孝凌。
- S:指定累計(jì)模式方咆。
- s:使top命令在安全模式中運(yùn)行。這將去除交互命令所帶來的潛在危險(xiǎn)蟀架。
- i:使top不顯示任何閑置或者僵死進(jìn)程瓣赂。
- c:顯示整個(gè)命令行而不只是顯示命令名。
命令說明
- jmx 快速發(fā)現(xiàn)jvm中的內(nèi)存異常項(xiàng)
【實(shí)戰(zhàn)階段】JVM排查問題優(yōu)化參數(shù)
jps [-q] [-mlvV] [<hostid>]
參數(shù)如下:
- -q 只顯示進(jìn)程號(hào)
- -m 顯示傳遞給main?法的參數(shù)
- -l 顯示應(yīng)?main class的完整包名應(yīng)?的jar?件完整路徑名
- -v 顯示傳遞給JVM的參數(shù)
- -V 禁?輸出類名辜窑、JAR?件名和傳遞給main?法的參數(shù)钩述,僅顯示本地JVM標(biāo)識(shí)符的列表
hostid的參數(shù)格式
- hostid:想要查看的主機(jī)的標(biāo)識(shí)符,格式為: [protocol:][[//]hostname][:port][/servername] 穆碎,其中:
- protocol:通信協(xié)議牙勘,默認(rèn)rmi
- hostname:?標(biāo)主機(jī)的主機(jī)名或IP地址
- port:通信端?,對(duì)于默認(rèn) rmi 協(xié)議所禀,該參數(shù)?來指定 rmiregistry 遠(yuǎn)程主機(jī)上的端?號(hào)方面。如省略該參數(shù),并且該
- protocol指示rmi色徘,則使?默認(rèn)使?1099端?恭金。
- servicename:服務(wù)名稱,取值取決于實(shí)現(xiàn)?式褂策,對(duì)于rmi協(xié)議横腿,此參數(shù)代表遠(yuǎn)程主機(jī)上RMI遠(yuǎn)程對(duì)象的名稱
今天就寫到這里颓屑,未完待續(xù),等待下一部分的內(nèi)容耿焊。