JVM性能優(yōu)化

Java 應(yīng)用性能優(yōu)化是一個(gè)老生常談的話題忍啤,典型的性能問題如頁面響應(yīng)慢、接口超時(shí)仙辟,服務(wù)器負(fù)載高同波、并發(fā)數(shù)低鳄梅,數(shù)據(jù)庫頻繁死鎖等。尤其是在“糙快猛”的互聯(lián)網(wǎng)開發(fā)模式大行其道的今天未檩,隨著系統(tǒng)訪問量的日益增加和代碼的臃腫卫枝,各種性能問題開始紛至沓來。Java 應(yīng)用性能的瓶頸點(diǎn)非常多讹挎,比如磁盤校赤、內(nèi)存、網(wǎng)絡(luò) I/O 等系統(tǒng)因素筒溃,Java 應(yīng)用代碼马篮,JVM GC,數(shù)據(jù)庫怜奖,緩存等浑测。筆者根據(jù)個(gè)人經(jīng)驗(yàn),將 Java 性能優(yōu)化分為 4 個(gè)層級:應(yīng)用層歪玲、數(shù)據(jù)庫層迁央、框架層、JVM 層滥崩。

Java 性能優(yōu)化分層模型

每層優(yōu)化難度逐級增加岖圈,涉及的知識和解決的問題也會(huì)不同。比如應(yīng)用層需要理解代碼邏輯钙皮,通過 Java 線程棧定位有問題代碼行等蜂科;數(shù)據(jù)庫層面需要分析 SQL、定位死鎖等短条;框架層需要懂源代碼导匣,理解框架機(jī)制;JVM 層需要對 GC 的類型和工作機(jī)制有深入了解茸时,對各種 JVM 參數(shù)作用了然于胸贡定。

對于調(diào)優(yōu)這個(gè)事情來說,一般就是三個(gè)過程:

性能監(jiān)控:問題沒有發(fā)生可都,你并不知道你需要調(diào)優(yōu)什么缓待。此時(shí)需要一些系統(tǒng)、應(yīng)用的監(jiān)控工具來發(fā)現(xiàn)問題汹粤。

性能分析:問題已經(jīng)發(fā)生命斧,但是你并不知道問題到底出在哪里。此時(shí)就需要使用工具嘱兼、經(jīng)驗(yàn)對系統(tǒng)国葬、應(yīng)用進(jìn)行瓶頸分析,以求定位到問題原因。

性能調(diào)優(yōu):經(jīng)過上一步的分析定位到了問題所在汇四,需要對問題進(jìn)行解決接奈,使用代碼、配置等手段進(jìn)行優(yōu)化通孽。

調(diào)優(yōu)準(zhǔn)備

調(diào)優(yōu)是需要做好準(zhǔn)備工作的序宦,畢竟每一個(gè)應(yīng)用的業(yè)務(wù)目標(biāo)都不盡相同,性能瓶頸也不會(huì)總在同一個(gè)點(diǎn)上背苦。在業(yè)務(wù)應(yīng)用層面互捌,我們需要:

需要了解系統(tǒng)的總體架構(gòu),明確壓力方向行剂。比如系統(tǒng)的哪一個(gè)接口秕噪、模塊是使用率最高的,面臨高并發(fā)的挑戰(zhàn)厚宰。

需要構(gòu)建測試環(huán)境來測試應(yīng)用的性能腌巾,使用ab、loadrunner铲觉、jmeter都可以澈蝙。

對關(guān)鍵業(yè)務(wù)數(shù)據(jù)量進(jìn)行分析,這里主要指的是對一些數(shù)據(jù)的量化分析撵幽,如數(shù)據(jù)庫一天的數(shù)據(jù)量有多少灯荧;緩存的數(shù)據(jù)量有多大等

了解系統(tǒng)的響應(yīng)速度、吞吐量并齐、TPS漏麦、QPS等指標(biāo)需求,比如秒殺系統(tǒng)對響應(yīng)速度和QPS的要求是非常高的况褪。

了解系統(tǒng)相關(guān)軟件的版本、模式和參數(shù)等更耻,有時(shí)候限于應(yīng)用依賴服務(wù)的版本测垛、模式等,性能也會(huì)受到一定的影響秧均。

性能分析

性能診斷一種是針對已經(jīng)確定有性能問題的系統(tǒng)和代碼進(jìn)行診斷食侮,還有一種是對預(yù)上線系統(tǒng)提前性能測試,確定性能是否符合上線要求目胡。針對前者锯七,性能診斷工具主要分為兩層:OS 層面和 Java 應(yīng)用層面(包括應(yīng)用代碼診斷和 GC 診斷),后者可以用各種性能壓測工具(例如 JMeter)進(jìn)行測試誉己。

OS 診斷

OS 的診斷主要關(guān)注的是 CPU眉尸、Memory、I/O 三個(gè)方面。

CPU 診斷

當(dāng)程序響應(yīng)變慢的時(shí)候噪猾,首先使用top霉祸、vmstat、ps等命令查看系統(tǒng)的cpu使用率是否有異常袱蜡,從而可以判斷出是否是cpu繁忙造成的性能問題丝蹭。其中,主要通過us(用戶進(jìn)程所占的%)這個(gè)數(shù)據(jù)來看異常的進(jìn)程信息坪蚁。當(dāng)us接近100%甚至更高時(shí)奔穿,可以確定是cpu繁忙造成的響應(yīng)緩慢。一般說來敏晤,cpu繁忙的原因有以下幾個(gè):

線程中有無限空循環(huán)贱田、無阻塞、正則匹配或者單純的計(jì)算

發(fā)生了頻繁的gc

多線程的上下文切換

對于 CPU 主要關(guān)注平均負(fù)載(Load Average)茵典,CPU 使用率湘换,上下文切換次數(shù)(Context Switch)。

通過 top 命令可以查看系統(tǒng)平均負(fù)載和 CPU 使用率统阿,圖為通過 top 命令查看某系統(tǒng)的狀態(tài)彩倚。

top -H -p [pid]

top 命令示例

平均負(fù)載有三個(gè)數(shù)字:63.66,58.39扶平,57.18帆离,分別表示過去 1 分鐘、5 分鐘结澄、15 分鐘機(jī)器的負(fù)載哥谷。按照經(jīng)驗(yàn),若數(shù)值小于 0.7*CPU 個(gè)數(shù)麻献,則系統(tǒng)工作正常们妥;若超過這個(gè)值,甚至達(dá)到 CPU 核數(shù)的四五倍勉吻,則系統(tǒng)的負(fù)載就明顯偏高监婶。圖中 15 分鐘負(fù)載已經(jīng)高達(dá) 57.18,1 分鐘負(fù)載是 63.66(系統(tǒng)為 16 核)齿桃,說明系統(tǒng)出現(xiàn)負(fù)載問題惑惶,且存在進(jìn)一步升高趨勢,需要定位具體原因了短纵。

確定好cpu使用率最高的進(jìn)程之后就可以使用jstack來打印出異常進(jìn)程的堆棧信息:

jstack [pid]

jstack命令示例

接下來需要注意的一點(diǎn)是带污,Linux下所有線程最終還是以輕量級進(jìn)程的形式存在系統(tǒng)中的,而使用jstack只能打印出進(jìn)程的信息香到,這些信息里面包含了此進(jìn)程下面所有線程(輕量級進(jìn)程-LWP)的堆棧信息鱼冀。因此报破,進(jìn)一步的需要確定是哪一個(gè)線程耗費(fèi)了大量cpu,此時(shí)可以使用top -p [processId]來查看雷绢,也可以直接通過ps -Le來顯示所有進(jìn)程,包括LWP的資源耗費(fèi)信息泛烙。最后,通過在jstack的輸出文件中查找對應(yīng)的lwp的id即可以定位到相應(yīng)的堆棧信息翘紊。其中需要注意的是線程的狀態(tài):RUNNABLE蔽氨、WAITING等。對于Runnable的進(jìn)程需要注意是否有耗費(fèi)cpu的計(jì)算帆疟。對于Waiting的線程一般是鎖的等待操作鹉究。

也可以使用jstat來查看對應(yīng)進(jìn)程的gc信息,以判斷是否是gc造成了cpu繁忙踪宠。

jstat -gcutil [pid]

jstat命令示例

還可以通過vmstat自赔,通過觀察內(nèi)核狀態(tài)的上下文切換(cs)次數(shù),來判斷是否是上下文切換造成的cpu繁忙:

vmstat 1 5

vmstat 命令示例

上下文切換次數(shù)發(fā)生的場景主要有如下幾種:1)時(shí)間片用完柳琢,CPU 正常調(diào)度下一個(gè)任務(wù)绍妨;2)被其它優(yōu)先級更高的任務(wù)搶占;3)執(zhí)行任務(wù)碰到 I/O 阻塞柬脸,掛起當(dāng)前任務(wù)他去,切換到下一個(gè)任務(wù);4)用戶代碼主動(dòng)掛起當(dāng)前任務(wù)讓出 CPU倒堕;5)多任務(wù)搶占資源灾测,由于沒有搶到被掛起;6)硬件中斷垦巴。Java 線程上下文切換主要來自共享資源的競爭媳搪。一般單個(gè)對象加鎖很少成為系統(tǒng)瓶頸,除非鎖粒度過大骤宣。但在一個(gè)訪問頻度高秦爆,對多個(gè)對象連續(xù)加鎖的代碼塊中就可能出現(xiàn)大量上下文切換,成為系統(tǒng)瓶頸憔披。

此外鲜结,有時(shí)候可能會(huì)由jit引起一些cpu飚高的情形,如大量方法編譯等活逆。這里可以使用-XX:+PrintCompilation這個(gè)參數(shù)輸出jit編譯情況,以排查jit編譯引起的cpu問題拗胜。

內(nèi)存診斷

從操作系統(tǒng)角度蔗候,內(nèi)存關(guān)注應(yīng)用進(jìn)程是否足夠,可以使用 free –m 命令查看內(nèi)存的使用情況埂软。通過 top 命令可以查看進(jìn)程使用的虛擬內(nèi)存 VIRT 和物理內(nèi)存 RES锈遥,根據(jù)公式 VIRT = SWAP + RES 可以推算出具體應(yīng)用使用的交換分區(qū)(Swap)情況纫事,使用交換分區(qū)過大會(huì)影響 Java 應(yīng)用性能,可以將 swappiness 值調(diào)到盡可能小所灸。因?yàn)閷τ?Java 應(yīng)用來說丽惶,占用太多交換分區(qū)可能會(huì)影響性能,畢竟磁盤性能比內(nèi)存慢太多爬立。

對Java應(yīng)用來說钾唬,內(nèi)存主要是由堆外內(nèi)存和堆內(nèi)內(nèi)存組成。

堆外內(nèi)存

堆外內(nèi)存主要是JNI侠驯、Deflater/Inflater抡秆、DirectByteBuffer(nio中會(huì)用到)使用的。對于這種堆外內(nèi)存的分析吟策,還是需要先通過vmstat儒士、sar、top檩坚、pidstat(這里的sar,pidstat以及iostat都是sysstat軟件套件的一部分着撩,需要單獨(dú)安裝)等查看swap和物理內(nèi)存的消耗狀況再做判斷的。此外匾委,對于JNI拖叙、Deflater這種調(diào)用可以通過Google-preftools來追蹤資源使用狀況。

堆內(nèi)內(nèi)存

此部分內(nèi)存為Java應(yīng)用主要的內(nèi)存區(qū)域剩檀。通常與這部分內(nèi)存性能相關(guān)的有:

創(chuàng)建的對象:這個(gè)是存儲(chǔ)在堆中的憋沿,需要控制好對象的數(shù)量和大小,尤其是大的對象很容易進(jìn)入老年代

全局集合:全局集合通常是生命周期比較長的沪猴,因此需要特別注意全局集合的使用

緩存:緩存選用的數(shù)據(jù)結(jié)構(gòu)不同辐啄,會(huì)很大程序影響內(nèi)存的大小和gc

ClassLoader:主要是動(dòng)態(tài)加載類容易造成永久代內(nèi)存不足

多線程:線程分配會(huì)占用本地內(nèi)存,過多的線程也會(huì)造成內(nèi)存不足

以上使用不當(dāng)很容易造成:

頻繁GC -> Stop the world运嗜,使你的應(yīng)用響應(yīng)變慢

OOM壶辜,直接造成內(nèi)存溢出錯(cuò)誤使得程序退出。OOM又可以分為以下幾種:

Heap space:堆內(nèi)存不足

PermGen space:永久代內(nèi)存不足

Native thread:本地線程沒有足夠內(nèi)存可分配

排查堆內(nèi)存問題的常用工具是jmap担租,是jdk自帶的砸民。一些常用用法如下:

查看jvm內(nèi)存使用狀況:jmap -heap

查看jvm內(nèi)存存活的對象:jmap -histo:live

把heap里所有對象都dump下來,無論對象是死是活:jmap -dump:format=b,file=xxx.hprof

先做一次full GC奋救,再dump岭参,只包含仍然存活的對象信息:jmap -dump:format=b,live,file=xxx.hprof

此外,不管是使用jmap還是在OOM時(shí)產(chǎn)生的dump文件尝艘,可以使用Eclipse的MAT(MEMORY ANALYZER TOOL)來分析演侯,可以看到具體的堆棧和內(nèi)存中對象的信息。當(dāng)然jdk自帶的jhat也能夠查看dump文件(啟動(dòng)web端口供開發(fā)者使用瀏覽器瀏覽堆內(nèi)對象的信息)背亥。此外秒际,VisualVM也能夠打開hprof文件悬赏,使用它的heap walker查看堆內(nèi)存信息。

I/O診斷

I/O 包括磁盤 I/O 和網(wǎng)絡(luò) I/O娄徊,一般情況下磁盤更容易出現(xiàn) I/O 瓶頸闽颇。通過 iostat 可以查看磁盤的讀寫情況,通過 CPU 的 I/O wait 可以看出磁盤 I/O 是否正常寄锐。如果磁盤 I/O 一直處于很高的狀態(tài)兵多,說明磁盤太慢或故障,成為了性能瓶頸锐峭,需要進(jìn)行應(yīng)用優(yōu)化或者磁盤更換中鼠。

文件IO

可以使用系統(tǒng)工具pidstat、iostat沿癞、vmstat來查看io的狀況援雇。這里可以看一張使用vmstat的結(jié)果圖。


vmstat命令示例

這里主要注意bi和bo這兩個(gè)值椎扬,分別表示塊設(shè)備每秒接收的塊數(shù)量和塊設(shè)備每秒發(fā)送的塊數(shù)量惫搏,由此可以判定io繁忙狀況。進(jìn)一步的可以通過使用strace工具定位對文件io的系統(tǒng)調(diào)用蚕涤。通常筐赔,造成文件io性能差的原因不外乎:

大量的隨機(jī)讀寫

設(shè)備慢

文件太大

網(wǎng)絡(luò)IO

查看網(wǎng)絡(luò)io狀況,一般使用的是netstat工具揖铜≤罘幔可以查看所有連接的狀況、數(shù)目天吓、端口信息等贿肩。例如:當(dāng)time_wait或者close_wait連接過多時(shí),會(huì)影響應(yīng)用的相應(yīng)速度龄寞。

netstat -anp

此外汰规,還可以使用tcpdump來具體分析網(wǎng)絡(luò)io的數(shù)據(jù)。當(dāng)然物邑,tcpdump出的文件直接打開是一堆二進(jìn)制的數(shù)據(jù)溜哮,可以使用wireshark閱讀具體的連接以及其中數(shù)據(jù)的內(nèi)容。

tcpdump -i eth0 -w tmp.cap -tnn dst port 8080 #監(jiān)聽8080端口的網(wǎng)絡(luò)請求并打印日志到tmp.cap中

還可以通過查看/proc/interrupts來獲取當(dāng)前系統(tǒng)使用的中斷的情況色解。

cat /proc/interrupts

各個(gè)列依次是:

irq的序號茂嗓, 在各自cpu上發(fā)生中斷的次數(shù),可編程中斷控制器科阎,設(shè)備名稱(request_irq的dev_name字段)

通過查看網(wǎng)卡設(shè)備的終端情況可以判斷網(wǎng)絡(luò)io的狀況在抛。

除了常用的 top、 ps萧恕、vmstat刚梭、iostat 等命令,還有其他 Linux 工具可以診斷系統(tǒng)問題票唆,如 mpstat朴读、tcpdump、netstat走趋、pidstat衅金、sar 等。Brendan 總結(jié)列出了 Linux 不同設(shè)備類型的性能診斷工具簿煌,如圖所示氮唯,可供參考。


Linux 性能觀測工具

Java 應(yīng)用診斷工具

應(yīng)用代碼診斷

應(yīng)用代碼性能問題是相對好解決的一類性能問題姨伟。通過一些應(yīng)用層面監(jiān)控報(bào)警惩琉,如果確定有問題的功能和代碼,直接通過代碼就可以定位夺荒;或者通過 top+jstack,找出有問題的線程棧,定位到問題線程的代碼上遥巴,也可以發(fā)現(xiàn)問題婿失。對于更復(fù)雜,邏輯更多的代碼段剿吻,通過 Stopwatch 打印性能日志往往也可以定位大多數(shù)應(yīng)用代碼性能問題窍箍。

常用的 Java 應(yīng)用診斷包括線程、堆棧丽旅、GC 等方面的診斷椰棘。

jstack

jstack 命令通常配合 top 使用,通過 top -H -p pid 定位 Java 進(jìn)程和線程魔招,再利用 jstack -l pid 導(dǎo)出線程棧晰搀。由于線程棧是瞬態(tài)的,因此需要多次 dump办斑,一般 3 次 dump外恕,一般每次隔 5s 就行。將 top 定位的 Java 線程 pid 轉(zhuǎn)成 16 進(jìn)制乡翅,得到 Java 線程棧中的 nid鳞疲,可以找到對應(yīng)的問題線程棧。

通過 top –H -p 查看運(yùn)行時(shí)間較長 Java 線程

如上圖所示蠕蚜,其中的線程 24985 運(yùn)行時(shí)間較長尚洽,可能存在問題,轉(zhuǎn)成 16 進(jìn)制后靶累,通過 Java 線程棧找到對應(yīng)線程 0x6199 的棧如下腺毫,從而定位問題點(diǎn)癣疟,如下圖所示。

jstack 查看線程堆棧

JProfiler

JProfiler 可對 CPU潮酒、堆睛挚、內(nèi)存進(jìn)行分析,功能強(qiáng)大急黎,如下圖所示扎狱。同時(shí)結(jié)合壓測工具,可以對代碼耗時(shí)采樣統(tǒng)計(jì)勃教。

通過 JProfiler 進(jìn)行內(nèi)存分析

GC 診斷

Java GC 解決了程序員管理內(nèi)存的風(fēng)險(xiǎn)淤击,但 GC 引起的應(yīng)用暫停成了另一個(gè)需要解決的問題。JDK 提供了一系列工具來定位 GC 問題故源,比較常用的有 jstat污抬、jmap,還有第三方工具 MAT 等心软。

jstat

jstat 命令可打印 GC 詳細(xì)信息壕吹,Young GC 和 Full GC 次數(shù),堆信息等删铃。其命令格式為

jstat –gcxxx -t pid 耳贬,如下圖所示。

jstat 命令示例

jmap

jmap 打印 Java 進(jìn)程堆信息 jmap –heap pid猎唁。通過 jmap –dump:file=xxx pid 可 dump 堆到文件咒劲,然后通過其它工具進(jìn)一步分析其堆使用情況

MAT

MAT 是 Java 堆的分析利器,提供了直觀的診斷報(bào)告诫隅,內(nèi)置的 OQL 允許對堆進(jìn)行類 SQL 查詢腐魂,功能強(qiáng)大,outgoing reference 和 incoming reference 可以對對象引用追根溯源逐纬。


MAT 示例

上圖是 MAT 使用示例蛔屹,MAT 有兩列顯示對象大小,分別是 Shallow size 和 Retained size豁生,前者表示對象本身占用內(nèi)存的大小兔毒,不包含其引用的對象,后者是對象自己及其直接或間接引用的對象的 Shallow size 之和甸箱,即該對象被回收后 GC 釋放的內(nèi)存大小育叁,一般說來關(guān)注后者大小即可。對于有些大堆 (幾十 G) 的 Java 應(yīng)用芍殖,需要較大內(nèi)存才能打開 MAT豪嗽。通常本地開發(fā)機(jī)內(nèi)存過小,是無法打開的,建議在線下服務(wù)器端安裝圖形環(huán)境和 MAT龟梦,遠(yuǎn)程打開查看隐锭。或者執(zhí)行 mat 命令生成堆索引变秦,拷貝索引到本地成榜,不過這種方式看到的堆信息有限。

為了診斷 GC 問題蹦玫,建議在 JVM 參數(shù)中加上-XX:+PrintGCDateStamps。常用的 GC 參數(shù)如下圖所示刘绣。

常用 GC 參數(shù)

對于 Java 應(yīng)用樱溉,通過 top+jstack+jmap+MAT 可以定位大多數(shù)應(yīng)用和內(nèi)存問題,可謂必備工具纬凤。有些時(shí)候福贞,Java 應(yīng)用診斷需要參考 OS 相關(guān)信息,可使用一些更全面的診斷工具停士,比如 Zabbix(整合了 OS 和 JVM 監(jiān)控)等挖帘。在分布式環(huán)境中,分布式跟蹤系統(tǒng)等基礎(chǔ)設(shè)施也對應(yīng)用性能診斷提供了有力支持恋技。

其他分析工具

上面分別針對CPU拇舀、內(nèi)存以及IO講了一些系統(tǒng)/JDK自帶的分析工具。除此之外蜻底,還有一些綜合分析工具或者框架可以更加方便我們對Java應(yīng)用性能的排查骄崩、分析、定位等薄辅。

VisualVM

這個(gè)工具應(yīng)該是Java開發(fā)者們非常熟悉的一款java應(yīng)用監(jiān)測工具要拂,原理是通過jmx接口來連接jvm進(jìn)程,從而能夠看到j(luò)vm上的線程站楚、內(nèi)存脱惰、類等信息。

Java Mission Control(jmc)

此工具是jdk7 u40開始自帶的窿春,原來是JRockit上的工具拉一,是一款采樣型的集診斷、分析和監(jiān)控與一體的非常強(qiáng)大的工具:https://docs.oracle.com/javacomponents/jmc-5-5/jmc-user-guide/toc.htm谁尸。但是此工具是基于JFR(jcmdJFR.start name=test duration=60s settings=template.jfc filename=output.jfr)的舅踪,而開啟JFR需要商業(yè)證書:jcmdVM.unlock_commercial_features。

Btrace

這里不得不提的是btrace這個(gè)神器良蛮,它使用java attach api+ java agent + instrument api能夠?qū)崿F(xiàn)jvm的動(dòng)態(tài)追蹤抽碌。在不重啟應(yīng)用的情況下可以加入攔截類的方法以打印日志等。具體的用法可以參考Btrace入門到熟練小工完全指南

Jwebap

Jwebap是一款JavaEE性能檢測框架货徙,基于asm增強(qiáng)字節(jié)碼實(shí)現(xiàn)左权。支持:http請求、jdbc連接痴颊、method的調(diào)用軌跡跟蹤以及次數(shù)赏迟、耗時(shí)的統(tǒng)計(jì)。由此可以獲取最耗時(shí)的請求蠢棱、方法锌杀,并可以查看jdbc連接的次數(shù)、是否關(guān)閉等泻仙。但此項(xiàng)目是2006年的一個(gè)項(xiàng)目糕再,已經(jīng)將近10年沒有更新。根據(jù)筆者使用玉转,已經(jīng)不支持jdk7編譯的應(yīng)用突想。如果要使用,建議基于原項(xiàng)目二次開發(fā)究抓,同時(shí)也可以加入對redis連接的軌跡跟蹤猾担。當(dāng)然,基于字節(jié)碼增強(qiáng)的原理刺下,也可以實(shí)現(xiàn)自己的JavaEE性能監(jiān)測框架绑嘹。

性能調(diào)優(yōu)

與性能分析相對應(yīng),性能調(diào)優(yōu)同樣分為三部分怠李。

CPU調(diào)優(yōu)

不要存在一直運(yùn)行的線程(無限while循環(huán))圾叼,可以使用sleep休眠一段時(shí)間。這種情況普遍存在于一些pull方式消費(fèi)數(shù)據(jù)的場景下捺癞,當(dāng)一次pull沒有拿到數(shù)據(jù)的時(shí)候建議sleep一下夷蚊,再做下一次pull。

輪詢的時(shí)候可以使用wait/notify機(jī)制

避免循環(huán)髓介、正則表達(dá)式匹配惕鼓、計(jì)算過多,包括使用String的format唐础、split箱歧、replace方法(可以使用apache的commons-lang里的StringUtils對應(yīng)的方法),使用正則去判斷郵箱格式(有時(shí)候會(huì)造成死循環(huán))一膨、序列/反序列化等呀邢。

結(jié)合jvm和代碼,避免產(chǎn)生頻繁的gc豹绪,尤其是full GC价淌。

此外,使用多線程的時(shí)候,還需要注意以下幾點(diǎn):

使用線程池蝉衣,減少線程數(shù)以及線程的切換

多線程對于鎖的競爭可以考慮減小鎖的粒度(使用ReetrantLock)括尸、拆分鎖(類似ConcurrentHashMap分bucket上鎖), 或者使用CAS、ThreadLocal病毡、不可變對象等無鎖技術(shù)濒翻。此外,多線程代碼的編寫最好使用jdk提供的并發(fā)包啦膜、Executors框架以及ForkJoin等有送,此外DiscuptorActor在合適的場景也可以使用。

內(nèi)存調(diào)優(yōu)

內(nèi)存的調(diào)優(yōu)主要就是對jvm的調(diào)優(yōu)僧家。

合理設(shè)置各個(gè)代的大小娶眷。避免新生代設(shè)置過小(不夠用,經(jīng)常minor gc并進(jìn)入老年代)以及過大(會(huì)產(chǎn)生碎片)啸臀,同樣也要避免Survivor設(shè)置過大和過小。

選擇合適的GC策略烁落。需要根據(jù)不同的場景選擇合適的gc策略乘粒。這里需要說的是,cms并非全能的伤塌。除非特別需要再設(shè)置灯萍,畢竟cms的新生代回收策略parnew并非最快的,且cms會(huì)產(chǎn)生碎片每聪。此外旦棉,G1直到j(luò)dk8的出現(xiàn)也并沒有得到廣泛應(yīng)用,并不建議使用药薯。

jvm啟動(dòng)參數(shù)配置-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:[log_path]绑洛,以記錄gc日志,便于排查問題童本。

其中真屯,對于第一點(diǎn),具體的還有一點(diǎn)建議:

年輕代大小選擇:響應(yīng)時(shí)間優(yōu)先的應(yīng)用穷娱,盡可能設(shè)大绑蔫,直到接近系統(tǒng)的最低響應(yīng)時(shí)間限制(根據(jù)實(shí)際情況選擇)。在此種情況下泵额,年輕代收集發(fā)生gc的頻率是最小的配深。同時(shí),也能夠減少到達(dá)年老代的對象嫁盲。吞吐量優(yōu)先的應(yīng)用篓叶,也盡可能的設(shè)置大,因?yàn)閷憫?yīng)時(shí)間沒有要求,垃圾收集可以并行進(jìn)行澜共,建議適合8CPU以上的應(yīng)用使用向叉。

年老代大小選擇:響應(yīng)時(shí)間優(yōu)先的應(yīng)用,年老代一般都是使用并發(fā)收集器嗦董,所以其大小需要小心設(shè)置母谎,一般要考慮并發(fā)會(huì)話率和會(huì)話持續(xù)時(shí)間等一些參數(shù)。如果堆設(shè)置小了京革,會(huì)造成內(nèi)存碎片奇唤、高回收頻率以及應(yīng)用暫停而使用傳統(tǒng)的標(biāo)記清除方式;如果堆大了匹摇,則需要較長的收集時(shí)間咬扇。最優(yōu)化的方案,一般需要參考以下數(shù)據(jù)獲得:

并發(fā)垃圾收集信息

持久代并發(fā)收集次數(shù)

傳統(tǒng)GC信息

花在年輕代和年老代回收上的時(shí)間比例

一般吞吐量優(yōu)先的應(yīng)用都應(yīng)該有一個(gè)很大的年輕代和一個(gè)較小的年老代廊勃。這樣可以盡可能回收掉大部分短期對象懈贺,減少中期的對象,而年老代存放長期存活對象坡垫。

此外梭灿,較小堆引起的碎片問題:因?yàn)槟昀洗牟l(fā)收集器使用標(biāo)記、清除算法冰悠,所以不會(huì)對堆進(jìn)行壓縮堡妒。當(dāng)收集器回收時(shí),會(huì)把相鄰的空間進(jìn)行合并溉卓,這樣可以分配給較大的對象皮迟。但是,當(dāng)堆空間較小時(shí)桑寨,運(yùn)行一段時(shí)間以后伏尼,就會(huì)出現(xiàn)“碎片”,如果并發(fā)收集器找不到足夠的空間西疤,那么并發(fā)收集器將會(huì)停止烦粒,然后使用傳統(tǒng)的標(biāo)記、清除方式進(jìn)行回收代赁。如果出現(xiàn)“碎片”扰她,可能需要進(jìn)行如下配置:-XX:+UseCMSCompactAtFullCollection,使用并發(fā)收集器時(shí)芭碍,開啟對年老代的壓縮徒役。同時(shí)使用-XX:CMSFullGCsBeforeCompaction=xx設(shè)置多少次Full GC后,對年老代進(jìn)行壓縮窖壕。

其余對于jvm的優(yōu)化問題可見后面JVM參數(shù)進(jìn)階一節(jié)忧勿。

代碼上杉女,也需要注意:

避免保存重復(fù)的String對象,同時(shí)也需要小心String.subString()與String.intern()的使用鸳吸,尤其是后者其底層數(shù)據(jù)結(jié)構(gòu)為StringTable熏挎,當(dāng)字符串大量不重復(fù)時(shí),會(huì)使得StringTable非常大(一個(gè)固定大小的hashmap晌砾,可以由參數(shù)-XX:StringTableSize=N設(shè)置大小)坎拐,從而影響young gc的速度。在jackson和fastjson中使用了此方法养匈,某些場景下會(huì)引起gc問題:YGC越來越慢哼勇,為什么

盡量不要使用finalizer

釋放不必要的引用:ThreadLocal使用完記得釋放以防止內(nèi)存泄漏呕乎,各種stream使用完也記得close积担。

使用對象池避免無節(jié)制創(chuàng)建對象,造成頻繁gc猬仁。但不要隨便使用對象池帝璧,除非像連接池、線程池這種初始化/創(chuàng)建資源消耗較大的場景湿刽,

緩存失效算法聋溜,可以考慮使用SoftReference、WeakReference保存緩存對象

謹(jǐn)慎熱部署/加載的使用叭爱,尤其是動(dòng)態(tài)加載類等

不要用Log4j輸出文件名、行號漱病,因?yàn)長og4j通過打印線程堆棧實(shí)現(xiàn)买雾,生成大量String。此外杨帽,使用log4j時(shí)漓穿,建議此種經(jīng)典用法,先判斷對應(yīng)級別的日志是否打開注盈,再做操作晃危,否則也會(huì)生成大量String。

if (logger.isInfoEnabled()) {

logger.info(msg);

}

IO調(diào)優(yōu)

文件IO上需要注意:

考慮使用異步寫入代替同步寫入老客,可以借鑒redis的aof機(jī)制僚饭。

利用緩存,減少隨機(jī)讀

盡量批量寫入胧砰,減少io次數(shù)和尋址

使用數(shù)據(jù)庫代替文件存儲(chǔ)

網(wǎng)絡(luò)IO上需要注意:

和文件IO類似鳍鸵,使用異步IO、多路復(fù)用IO/事件驅(qū)動(dòng)IO代替同步阻塞IO

批量進(jìn)行網(wǎng)絡(luò)IO,減少IO次數(shù)

使用緩存尉间,減少對網(wǎng)絡(luò)數(shù)據(jù)的讀取

使用協(xié)程:Quasar

其他優(yōu)化建議

算法偿乖、邏輯上是程序性能的首要击罪,遇到性能問題,應(yīng)該首先優(yōu)化程序的邏輯處理

優(yōu)先考慮使用返回值而不是異常表示錯(cuò)誤

查看自己的代碼是否對內(nèi)聯(lián)是友好的:你的Java代碼對JIT編譯友好么贪薪?

此外媳禁,jdk7、8在jvm的性能上做了一些增強(qiáng):

通過-XX:+TieredCompilation開啟JDK7的多層編譯(tiered compilation)支持画切。多層編譯結(jié)合了客戶端C1編譯器和服務(wù)端C2編譯器的優(yōu)點(diǎn)(客戶端編譯能夠快速啟動(dòng)和及時(shí)優(yōu)化竣稽,服務(wù)器端編譯可以提供更多的高級優(yōu)化),是一個(gè)非常高效利用資源的切面方案槽唾。在開始時(shí)先進(jìn)行低層次的編譯丧枪,同時(shí)收集信息,在后期再進(jìn)一步進(jìn)行高層次的編譯進(jìn)行高級優(yōu)化庞萍。需要注意的一點(diǎn):這個(gè)參數(shù)會(huì)消耗比較多的內(nèi)存資源拧烦,因?yàn)橥粋€(gè)方法被編譯了多次,存在多份native內(nèi)存拷貝钝计,建議把code cache調(diào)大一點(diǎn)兒(-XX:+ReservedCodeCacheSize恋博,InitialCodeCacheSize)。否則有可能由于code cache不足私恬,jit編譯的時(shí)候不停的嘗試清理code cache债沮,丟棄無用方法,消耗大量資源在jit線程上本鸣。

Compressed Oops:壓縮指針在jdk7中的server模式下已經(jīng)默認(rèn)開啟疫衩。

Zero-Based Compressed Ordinary Object Pointers:當(dāng)使用了上述的壓縮指針時(shí),在64位jvm上荣德,會(huì)要求操作系統(tǒng)保留從一個(gè)虛擬地址0開始的內(nèi)存闷煤。如果操作系統(tǒng)支持這種請求,那么就開啟了Zero-Based Compressed Oops涮瞻。這樣可以使得無須在java堆的基地址添加任何地址補(bǔ)充即可把一個(gè)32位對象的偏移解碼成64位指針鲤拿。

逃逸分析(Escape Analysis): Server模式的編譯器會(huì)根據(jù)代碼的情況,來判斷相關(guān)對象的逃逸類型署咽,從而決定是否在堆中分配空間近顷,是否進(jìn)行標(biāo)量替換(在棧上分配原子類型局部變量)。此外宁否,也可以根據(jù)調(diào)用情況來決定是否自動(dòng)消除同步控制窒升,如StringBuffer。這個(gè)特性從Java SE 6u23開始就默認(rèn)開啟慕匠。

NUMA Collector Enhancements:這個(gè)重要針對的是The Parallel Scavenger垃圾回收器异剥。使其能夠利用NUMA (Non Uniform Memory Access,即每一個(gè)處理器核心都有本地內(nèi)存絮重,能夠低延遲冤寿、高帶寬訪問) 架構(gòu)的機(jī)器的優(yōu)勢來更快的進(jìn)行g(shù)c歹苦。可以通過-XX:+UseNUMA開啟支持督怜。

此外殴瘦,網(wǎng)上還有很多過時(shí)的建議,不要再盲目跟隨:

變量用完設(shè)置為null号杠,加快內(nèi)存回收蚪腋,這種用法大部分情況下并沒有意義。一種情況除外:如果有個(gè)Java方法沒有被JIT編譯但里面仍然有代碼會(huì)執(zhí)行比較長時(shí)間姨蟋,那么在那段會(huì)執(zhí)行長時(shí)間的代碼前顯式將不需要的引用類型局部變量置null是可取的屉凯。具體的可以見R大的解釋:https://www.zhihu.com/question/48059457/answer/113538171

方法參數(shù)設(shè)置為final,這種用法也沒有太大的意義眼溶,尤其在jdk8中引入了effective final悠砚,會(huì)自動(dòng)識別final變量。

JVM內(nèi)存調(diào)優(yōu)Tips

如何將新對象預(yù)留在年輕代

眾所周知堂飞,由于 Full GC 的成本遠(yuǎn)遠(yuǎn)高于 Minor GC灌旧,因此某些情況下需要盡可能將對象分配在年輕代,這在很多情況下是一個(gè)明智的選擇绰筛。雖然在大部分情況下枢泰,JVM 會(huì)嘗試在 Eden 區(qū)分配對象,但是由于空間緊張等問題铝噩,很可能不得不將部分年輕對象提前向年老代壓縮衡蚂。因此,在 JVM 參數(shù)調(diào)優(yōu)時(shí)可以為應(yīng)用程序分配一個(gè)合理的年輕代空間骏庸,以最大限度避免新對象直接進(jìn)入年老代的情況發(fā)生讳窟。

分配足夠大的年輕代空間,使用 JVM 參數(shù)-XX:+PrintGCDetails -Xmx20M -Xms20M-Xmn6M

如何讓大對象進(jìn)入年老代

我們在大部分情況下都會(huì)選擇將對象分配在年輕代敞恋。但是,對于占用內(nèi)存較多的大對象而言谋右,它的選擇可能就不是這樣的硬猫。因?yàn)榇髮ο蟪霈F(xiàn)在年輕代很可能擾亂年輕代 GC,并破壞年輕代原有的對象結(jié)構(gòu)改执。因?yàn)閲L試在年輕代分配大對象啸蜜,很可能導(dǎo)致空間不足,為了有足夠的空間容納大對象辈挂,JVM 不得不將年輕代中的年輕對象挪到年老代衬横。因?yàn)榇髮ο笳加每臻g多,所以可能需要移動(dòng)大量小的年輕對象進(jìn)入年老代终蒂,這對 GC 相當(dāng)不利蜂林∫K撸基于以上原因,可以將大對象直接分配到年老代噪叙,保持年輕代對象結(jié)構(gòu)的完整性矮锈,這樣可以提高 GC 的效率。如果一個(gè)大對象同時(shí)又是一個(gè)短命的對象睁蕾,假設(shè)這種情況出現(xiàn)很頻繁苞笨,那對于 GC 來說會(huì)是一場災(zāi)難。原本應(yīng)該用于存放永久對象的年老代子眶,被短命的對象塞滿瀑凝,這也意味著對堆空間進(jìn)行了洗牌,擾亂了分代內(nèi)存回收的基本思路臭杰。因此粤咪,在軟件開發(fā)過程中,應(yīng)該盡可能避免使用短命的大對象硅卢。

可以使用參數(shù)-XX:PetenureSizeThreshold 設(shè)置大對象直接進(jìn)入年老代的閾值射窒。當(dāng)對象的大小超過這個(gè)值時(shí),將直接在年老代分配将塑。參數(shù)-XX:PetenureSizeThreshold 只對串行收集器和年輕代并行收集器有效脉顿,并行回收收集器不識別這個(gè)參數(shù)。

如何設(shè)置對象進(jìn)入年老代的年齡

堆中的每一個(gè)對象都有自己的年齡点寥。一般情況下艾疟,年輕對象存放在年輕代,年老對象存放在年老代敢辩。為了做到這點(diǎn)蔽莱,虛擬機(jī)為每個(gè)對象都維護(hù)一個(gè)年齡。如果對象在 Eden 區(qū)戚长,經(jīng)過一次 GC 后依然存活盗冷,則被移動(dòng)到 Survivor 區(qū)中,對象年齡加 1同廉。以后仪糖,如果對象每經(jīng)過一次 GC 依然存活,則年齡再加 1迫肖。當(dāng)對象年齡達(dá)到閾值時(shí)锅劝,就移入年老代,成為老年對象蟆湖。這個(gè)閾值的最大值可以通過參數(shù)-XX:MaxTenuringThreshold 來設(shè)置故爵,默認(rèn)值是 15。雖然-XX:MaxTenuringThreshold 的值可能是 15 或者更大隅津,但這不意味著新對象非要達(dá)到這個(gè)年齡才能進(jìn)入年老代诬垂。事實(shí)上劲室,對象實(shí)際進(jìn)入年老代的年齡是虛擬機(jī)在運(yùn)行時(shí)根據(jù)內(nèi)存使用情況動(dòng)態(tài)計(jì)算的,這個(gè)參數(shù)指定的是閾值年齡的最大值剥纷。即痹籍,實(shí)際晉升年老代年齡等于動(dòng)態(tài)計(jì)算所得的年齡與-XX:MaxTenuringThreshold 中較小的那個(gè)。

參數(shù)為-XX:+PrintGCDetails -Xmx20M -Xms20M -Xmn10M -XX:SurvivorRatio=2 -XX:MaxTenuringThreshold=1

穩(wěn)定的 Java 堆 VS 動(dòng)蕩的 Java 堆

一般來說晦鞋,穩(wěn)定的堆大小對垃圾回收是有利的名惩。獲得一個(gè)穩(wěn)定的堆大小的方法是使-Xms 和-Xmx 的大小一致焙矛,即最大堆和最小堆 (初始堆) 一樣。如果這樣設(shè)置,系統(tǒng)在運(yùn)行時(shí)堆大小理論上是恒定的力细,穩(wěn)定的堆空間可以減少 GC 的次數(shù)谍珊。因此诅蝶,很多服務(wù)端應(yīng)用都會(huì)將最大堆和最小堆設(shè)置為相同的數(shù)值候生。但是,一個(gè)不穩(wěn)定的堆并非毫無用處湾趾。穩(wěn)定的堆大小雖然可以減少 GC 次數(shù)芭商,但同時(shí)也增加了每次 GC 的時(shí)間。讓堆大小在一個(gè)區(qū)間中震蕩搀缠,在系統(tǒng)不需要使用大內(nèi)存時(shí)铛楣,壓縮堆空間,使 GC 應(yīng)對一個(gè)較小的堆艺普,可以加快單次 GC 的速度簸州。基于這樣的考慮歧譬,JVM 還提供了兩個(gè)參數(shù)用于壓縮和擴(kuò)展堆空間岸浑。

-XX:MinHeapFreeRatio 參數(shù)用來設(shè)置堆空間最小空閑比例,默認(rèn)值是 40瑰步。當(dāng)堆空間的空閑內(nèi)存小于這個(gè)數(shù)值時(shí)矢洲,JVM 便會(huì)擴(kuò)展堆空間。

-XX:MaxHeapFreeRatio 參數(shù)用來設(shè)置堆空間最大空閑比例缩焦,默認(rèn)值是 70读虏。當(dāng)堆空間的空閑內(nèi)存大于這個(gè)數(shù)值時(shí),便會(huì)壓縮堆空間舌界,得到一個(gè)較小的堆。

當(dāng)-Xmx 和-Xms 相等時(shí)泰演,-XX:MinHeapFreeRatio 和-XX:MaxHeapFreeRatio 兩個(gè)參數(shù)無效呻拌。

增大吞吐量提升系統(tǒng)性能

吞吐量優(yōu)先的方案將會(huì)盡可能減少系統(tǒng)執(zhí)行垃圾回收的總時(shí)間,故可以考慮關(guān)注系統(tǒng)吞吐量的并行回收收集器睦焕。在擁有高性能的計(jì)算機(jī)上藐握,進(jìn)行吞吐量優(yōu)先優(yōu)化靴拱,可以使用參數(shù):

java –Xmx3800m –Xms3800m –Xmn2G –Xss128k –XX:+UseParallelGC

–XX:ParallelGC-Threads=20 –XX:+UseParallelOldGC

–Xmx380m –Xms3800m:設(shè)置 Java 堆的最大值和初始值。一般情況下猾普,為了避免堆內(nèi)存的頻繁震蕩袜炕,導(dǎo)致系統(tǒng)性能下降,我們的做法是設(shè)置最大堆等于最小堆初家。假設(shè)這里把最小堆減少為最大堆的一半偎窘,即 1900m,那么 JVM 會(huì)盡可能在 1900MB 堆空間中運(yùn)行溜在,如果這樣陌知,發(fā)生 GC 的可能性就會(huì)比較高;

-Xss128k:減少線程棧的大小掖肋,這樣可以使剩余的系統(tǒng)內(nèi)存支持更多的線程仆葡;

-Xmn2g:設(shè)置年輕代區(qū)域大小為 2GB;

–XX:+UseParallelGC:年輕代使用并行垃圾回收收集器志笼。這是一個(gè)關(guān)注吞吐量的收集器沿盅,可以盡可能地減少 GC 時(shí)間。

–XX:ParallelGC-Threads:設(shè)置用于垃圾回收的線程數(shù)纫溃,通常情況下腰涧,可以設(shè)置和 CPU 數(shù)量相等。但在 CPU 數(shù)量比較多的情況下皇耗,設(shè)置相對較小的數(shù)值也是合理的南窗;

–XX:+UseParallelOldGC:設(shè)置年老代使用并行回收收集器。

嘗試使用大的內(nèi)存分頁

CPU 是通過尋址來訪問內(nèi)存的郎楼。32 位 CPU 的尋址寬度是 0~0xFFFFFFFF 万伤,計(jì)算后得到的大小是 4G,也就是說可支持的物理內(nèi)存最大是 4G呜袁。但在實(shí)踐過程中敌买,碰到了這樣的問題,程序需要使用 4G 內(nèi)存阶界,而可用物理內(nèi)存小于 4G虹钮,導(dǎo)致程序不得不降低內(nèi)存占用。為了解決此類問題膘融,現(xiàn)代 CPU 引入了 MMU(Memory Management Unit 內(nèi)存管理單元)芙粱。MMU 的核心思想是利用虛擬地址替代物理地址,即 CPU 尋址時(shí)使用虛址氧映,由 MMU 負(fù)責(zé)將虛址映射為物理地址春畔。MMU 的引入,解決了對物理內(nèi)存的限制,對程序來說律姨,就像自己在使用 4G 內(nèi)存一樣振峻。內(nèi)存分頁 (Paging) 是在使用 MMU 的基礎(chǔ)上,提出的一種內(nèi)存管理機(jī)制择份。它將虛擬地址和物理地址按固定大锌勖稀(4K)分割成頁 (page) 和頁幀 (page frame),并保證頁與頁幀的大小相同荣赶。這種機(jī)制凤价,從數(shù)據(jù)結(jié)構(gòu)上,保證了訪問內(nèi)存的高效讯壶,并使 OS 能支持非連續(xù)性的內(nèi)存分配料仗。在程序內(nèi)存不夠用時(shí),還可以將不常用的物理內(nèi)存頁轉(zhuǎn)移到其他存儲(chǔ)設(shè)備上伏蚊,比如磁盤立轧,這就是大家耳熟能詳?shù)奶摂M內(nèi)存。

在 Solaris 系統(tǒng)中躏吊,JVM 可以支持 Large Page Size 的使用氛改。使用大的內(nèi)存分頁可以增強(qiáng) CPU 的內(nèi)存尋址能力,從而提升系統(tǒng)的性能比伏。

java –Xmx2506m –Xms2506m –Xmn1536m –Xss128k –XX:++UseParallelGC

–XX:ParallelGCThreads=20 –XX:+UseParallelOldGC –XX:+LargePageSizeInBytes=256m

–XX:+LargePageSizeInBytes:設(shè)置大頁的大小胜卤。

過大的內(nèi)存分頁會(huì)導(dǎo)致 JVM 在計(jì)算 Heap 內(nèi)部分區(qū)(perm, new, old)內(nèi)存占用比例時(shí),會(huì)出現(xiàn)超出正常值的劃分赁项,最壞情況下某個(gè)區(qū)會(huì)多占用一個(gè)頁的大小葛躏。

使用非占有的垃圾回收器

為降低應(yīng)用軟件的垃圾回收時(shí)的停頓,首先考慮的是使用關(guān)注系統(tǒng)停頓的 CMS 回收器悠菜,其次舰攒,為了減少 Full GC 次數(shù),應(yīng)盡可能將對象預(yù)留在年輕代悔醋,因?yàn)槟贻p代 Minor GC 的成本遠(yuǎn)遠(yuǎn)小于年老代的 Full GC摩窃。

java –Xmx3550m –Xms3550m –Xmn2g –Xss128k –XX:ParallelGCThreads=20

–XX:+UseConcMarkSweepGC –XX:+UseParNewGC –XX:+SurvivorRatio=8 –XX:TargetSurvivorRatio=90

–XX:MaxTenuringThreshold=31

–XX:ParallelGCThreads=20:設(shè)置 20 個(gè)線程進(jìn)行垃圾回收;

–XX:+UseParNewGC:年輕代使用并行回收器芬骄;

–XX:+UseConcMarkSweepGC:年老代使用 CMS 收集器降低停頓猾愿;

–XX:+SurvivorRatio:設(shè)置 Eden 區(qū)和 Survivor 區(qū)的比例為 8:1。稍大的 Survivor 空間可以提高在年輕代回收生命周期較短的對象的可能性账阻,如果 Survivor 不夠大蒂秘,一些短命的對象可能直接進(jìn)入年老代,這對系統(tǒng)來說是不利的淘太。

–XX:TargetSurvivorRatio=90:設(shè)置 Survivor 區(qū)的可使用率姻僧。這里設(shè)置為 90%观挎,則允許 90%的 Survivor 空間被使用。默認(rèn)值是 50%段化。故該設(shè)置提高了 Survivor 區(qū)的使用率。當(dāng)存放的對象超過這個(gè)百分比造成,則對象會(huì)向年老代壓縮显熏。因此,這個(gè)選項(xiàng)更有助于將對象留在年輕代晒屎。

–XX:MaxTenuringThreshold:設(shè)置年輕對象晉升到年老代的年齡喘蟆。默認(rèn)值是 15 次,即對象經(jīng)過 15 次 Minor GC 依然存活鼓鲁,則進(jìn)入年老代蕴轨。這里設(shè)置為 31,目的是讓對象盡可能地保存在年輕代區(qū)域骇吭。

總結(jié)與建議

性能調(diào)優(yōu)同樣遵循 2-8 原則橙弱,80%的性能問題是由 20%的代碼產(chǎn)生的,因此優(yōu)化關(guān)鍵代碼事半功倍燥狰。同時(shí)棘脐,對性能的優(yōu)化要做到按需優(yōu)化,過度優(yōu)化可能引入更多問題龙致。對于 Java 性能優(yōu)化蛀缝,不僅要理解系統(tǒng)架構(gòu)、應(yīng)用代碼目代,同樣需要關(guān)注 JVM 層甚至操作系統(tǒng)底層屈梁。總結(jié)起來主要可以從以下幾點(diǎn)進(jìn)行考慮:

1)基礎(chǔ)性能的調(diào)優(yōu)

這里的基礎(chǔ)性能指的是硬件層級或者操作系統(tǒng)層級的升級優(yōu)化榛了,比如網(wǎng)絡(luò)調(diào)優(yōu)在讶,操作系統(tǒng)版本升級,硬件設(shè)備優(yōu)化等忽冻。比如 F5 的使用和 SDD 硬盤的引入真朗,包括新版本 Linux 在 NIO 方面的升級,都可以極大的促進(jìn)應(yīng)用的性能提升僧诚;

2)數(shù)據(jù)庫性能優(yōu)化

包括常見的事務(wù)拆分遮婶,索引調(diào)優(yōu),SQL 優(yōu)化湖笨,NoSQL 引入等旗扑,比如在事務(wù)拆分時(shí)引入異步化處理,最終達(dá)到一致性等做法的引入慈省,包括在針對具體場景引入的各類 NoSQL 數(shù)據(jù)庫臀防,都可以大大緩解傳統(tǒng)數(shù)據(jù)庫在高并發(fā)下的不足;

3)應(yīng)用架構(gòu)優(yōu)化

引入一些新的計(jì)算或者存儲(chǔ)框架,利用新特性解決原有集群計(jì)算性能瓶頸等袱衷;或者引入分布式策略捎废,在計(jì)算和存儲(chǔ)進(jìn)行水平化,包括提前計(jì)算預(yù)處理等致燥,利用典型的空間換時(shí)間的做法等登疗;都可以在一定程度上降低系統(tǒng)負(fù)載;

4)業(yè)務(wù)層面的優(yōu)化

技術(shù)并不是提升系統(tǒng)性能的唯一手段嫌蚤,在很多出現(xiàn)性能問題的場景中辐益,其實(shí)可以看到很大一部分都是因?yàn)樘厥獾臉I(yè)務(wù)場景引起的,如果能在業(yè)務(wù)上進(jìn)行規(guī)避或者調(diào)整脱吱,其實(shí)往往是最有效的智政。

參考

Java 應(yīng)用性能調(diào)優(yōu)實(shí)踐

JVM 優(yōu)化經(jīng)驗(yàn)總結(jié)

Java調(diào)優(yōu)經(jīng)驗(yàn)談

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市箱蝠,隨后出現(xiàn)的幾起案子续捂,更是在濱河造成了極大的恐慌,老刑警劉巖宦搬,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疾忍,死亡現(xiàn)場離奇詭異,居然都是意外死亡床三,警方通過查閱死者的電腦和手機(jī)一罩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撇簿,“玉大人聂渊,你說我怎么就攤上這事∷奶保” “怎么了汉嗽?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長找蜜。 經(jīng)常有香客問我饼暑,道長,這世上最難降的妖魔是什么洗做? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任弓叛,我火速辦了婚禮,結(jié)果婚禮上诚纸,老公的妹妹穿的比我還像新娘撰筷。我一直安慰自己,他們只是感情好畦徘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布毕籽。 她就那樣靜靜地躺著抬闯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪关筒。 梳的紋絲不亂的頭發(fā)上溶握,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機(jī)與錄音蒸播,去河邊找鬼奈虾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛廉赔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匾鸥,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼蜡塌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了勿负?” 一聲冷哼從身側(cè)響起馏艾,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎奴愉,沒想到半個(gè)月后琅摩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锭硼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年房资,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檀头。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轰异,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出暑始,到底是詐尸還是另有隱情搭独,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布廊镜,位于F島的核電站牙肝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嗤朴。R本人自食惡果不足惜配椭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望雹姊。 院中可真熱鬧颂郎,春花似錦、人聲如沸容为。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至替劈,卻和暖如春寄雀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陨献。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工盒犹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人眨业。 一個(gè)月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓急膀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親龄捡。 傳聞我的和親對象是個(gè)殘疾皇子卓嫂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容