摘要:前言入門垃圾回收機(jī)制后,接下來可以學(xué)習(xí)性能調(diào)優(yōu)了。輸出老年代空間的性能數(shù)據(jù)。新生代最小空間容量,單位。擁有者表示線程成功競爭到對象鎖。線程狀態(tài),未啟動(dòng)的。,無限期等待另一個(gè)線程執(zhí)行特定操作。主要調(diào)優(yōu)參數(shù)設(shè)定堆內(nèi)存大小,這是最基本的。
前言Java程序員進(jìn)階三條必經(jīng)之路:數(shù)據(jù)庫、虛擬機(jī)、異步通信。
入門JVM垃圾回收機(jī)制后,接下來可以學(xué)習(xí)性能調(diào)優(yōu)了。主要有兩部分內(nèi)容:
JDK工具的使用。
調(diào)優(yōu)策略。
兵器譜 jps列出正在運(yùn)行的虛擬機(jī)進(jìn)程,用法如下:
jps [-option] [hostid]
| 選項(xiàng) | 作用 |
| -------- | -----: |
| q | 只輸出LVMID,省略主類的名稱 |
| m | 輸出main method的參數(shù) |
| l | 輸出完全的包名,應(yīng)用主類名,jar的完全路徑名 |
| v | 輸出jvm參數(shù)?|
監(jiān)視虛擬機(jī)運(yùn)行狀態(tài)信息,使用方式:
jstat -
| 選項(xiàng) | 作用 |
| -------- | -----: |
| gc | 輸出每個(gè)堆區(qū)域的當(dāng)前可用空間以及已用空間,GC執(zhí)行的總次數(shù),GC操作累計(jì)所花費(fèi)的時(shí)間。|
| gccapactiy | 輸出每個(gè)堆區(qū)域的最小空間限制(ms)/最大空間限制(mx),當(dāng)前大小,每個(gè)區(qū)域之上執(zhí)行GC的次數(shù)。(不輸出當(dāng)前已用空間以及GC執(zhí)行時(shí)間)。|
| gccause | 輸出-gcutil提供的信息以及最后一次執(zhí)行GC的發(fā)生原因和當(dāng)前所執(zhí)行的GC的發(fā)生原因。 |
| gcnew | 輸出新生代空間的GC性能數(shù)據(jù)。|
| gcnewcapacity | 輸出新生代空間的大小的統(tǒng)計(jì)數(shù)據(jù)。|
| gcold | 輸出老年代空間的GC性能數(shù)據(jù)。|
| gcoldcapacity | 輸出老年代空間的大小的統(tǒng)計(jì)數(shù)據(jù)。|
| gcpermcapacity | 輸出持久帶空間的大小的統(tǒng)計(jì)數(shù)據(jù)。|
| gcutil | 輸出每個(gè)堆區(qū)域使用占比,以及GC執(zhí)行的總次數(shù)和GC操作所花費(fèi)的事件。|
比如:
jstat -gc 28389 1s
每隔1秒輸出一次JVM運(yùn)行信息:
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 52416.0 52416.0 4744.9 0.0 419456.0 28180.6 2621440.0 439372.6 131072.0 33564.8 160472 1760.603 61 2.731 1763.334
| 列 | 說明 | jstat參數(shù) |
| -------- | -----: | -----: |
| S0C | Survivor0空間的大小。單位KB。| -gc -gccapacity -gcnew -gcnewcapacity |
| S1C | Survivor1空間的大小。單位KB。| -gc -gccapacity -gcnew -gcnewcapacity |
| S0U | Survivor0已用空間的大小。單位KB。| -gc -gcnew |
| S1U | Survivor1已用空間的大小。單位KB。| -gc -gcnew |
| EC | Eden空間的大小。單位KB。| -gc -gccapacity -gcnew -gcnewcapacity |
| EU | Eden已用空間的大小。單位KB。| -gc-gcnew |
| OC | 老年代空間的大小。單位KB。| -gc -gccapacity -gcold -gcoldcapacity |
| OU | 老年代已用空間的大小。單位KB。| -gc -gcold |
| PC | 持久代空間的大小。單位KB。| -gc -gccapacity -gcold -gcoldcapacity -gcpermcapacity |
| PU | 持久代已用空間的大小。單位KB。| -gc -gcold |
| YGC | 新生代空間GC時(shí)間發(fā)生的次數(shù)。| -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| YGCT | 新生代GC處理花費(fèi)的時(shí)間。| -gc-gcnew-gcutil-gccause |
| FGC | full GC發(fā)生的次數(shù)。| -gc -gccapacity -gcnew -gcnewcapacity -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| FGCT | full GC操作花費(fèi)的時(shí)間。| -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| GCT | GC操作花費(fèi)的總時(shí)間。| -gc -gcold -gcoldcapacity -gcpermcapacity -gcutil -gccause |
| NGCMN | 新生代最小空間容量,單位KB。| -gccapacity -gcnewcapacity |
| NGCMX | 新生代最大空間容量,單位KB。| -gccapacity -gcnewcapacity |
| NGC | 新生代當(dāng)前空間容量,單位KB。| -gccapacity -gcnewcapacity |
| OGCMN | 老年代最小空間容量,單位KB。 | -gccapacity-gcoldcapacity |
| OGCMX | 老年代最大空間容量,單位KB。| -gccapacity-gcoldcapacity |
| OGC | 老年代當(dāng)前空間容量制,單位KB。| -gccapacity -gcoldcapacity |
| PGCMN | 持久代最小空間容量,單位KB。| -gccapacity -gcpermcapacity |
| PGCMX | 持久代最大空間容量,單位KB。| -gccapacity -gcpermcapacity |
| PGC | 持久代當(dāng)前空間容量,單位KB。| -gccapacity -gcpermcapacity |
| PC | 持久代當(dāng)前空間大小,單位KB。| -gccapacity-gcpermcapacity |
| PU | 持久代當(dāng)前已用空間大小,單位KB。| -gc -gcold |
| LGCC | 最后一次GC發(fā)生的原因。| -gccause |
| GCC | 當(dāng)前GC發(fā)生的原因。| -gccause |
| TT | 老年化閾值。被移動(dòng)到老年代之前,在新生代空存活的次數(shù)。| -gcnew |
| MTT | 最大老年化閾值。被移動(dòng)到老年代之前,在新生代空存活的次數(shù)。| -gcnew |
| DSS | 幸存者區(qū)所需空間大小,單位KB。| -gcnew |
生成堆存儲(chǔ)快照,使用方式:
jmap [ -option ]
| 選項(xiàng) | 作用 |
| -------- | -----: |
| dump | 生成堆存儲(chǔ)快照,格式為:-dump:[live, ]format=b, file=
| heap | 顯示java堆詳細(xì)信息,如使用那種回收器、參數(shù)配置、分代狀況等。|
| histo | 顯示堆中對象統(tǒng)計(jì)信息,包括類、實(shí)例數(shù)量、合計(jì)容量。|
生成虛擬機(jī)當(dāng)前時(shí)刻的線程快照,幫助定位線程出現(xiàn)長時(shí)間停頓的原因,用法:
jstack
Monitor
Monitor是 Java中用以實(shí)現(xiàn)線程之間的互斥與協(xié)作的主要手段,它可以看成是對象或者Class的鎖。每一個(gè)對象都有,也僅有一個(gè) monitor。下面這個(gè)圖,描述了線程和 Monitor之間關(guān)系,以及線程的狀態(tài)轉(zhuǎn)換圖:
進(jìn)入?yún)^(qū)(Entrt Set):表示線程通過synchronized要求獲取對象的鎖,但并未得到。
擁有者(The Owner):表示線程成功競爭到對象鎖。
等待區(qū)(Wait Set):表示線程通過對象的wait方法,釋放對象的鎖,并在等待區(qū)等待被喚醒。
線程狀態(tài)
NEW,未啟動(dòng)的。不會(huì)出現(xiàn)在Dump中。
RUNNABLE,在虛擬機(jī)內(nèi)執(zhí)行的。
BLOCKED,等待獲得監(jiān)視器鎖。
WATING,無限期等待另一個(gè)線程執(zhí)行特定操作。
TIMED_WATING,有時(shí)限的等待另一個(gè)線程的特定操作。
TERMINATED,已退出的。
舉個(gè)例子:
package com.jiuyan.mountain.test; import java.util.concurrent.TimeUnit; /** * Hello world! * */ public class App { public static void main(String[] args) throws InterruptedException { MyTask task = new MyTask(); Thread t1 = new Thread(task); t1.setName("t1"); Thread t2 = new Thread(task); t2.setName("t2"); t1.start(); t2.start(); } } class MyTask implements Runnable { private Integer mutex; public MyTask() { mutex = 1; } @Override public void run() { synchronized (mutex) { while(true) { System.out.println(Thread.currentThread().getName()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
線程狀態(tài):
"t2" prio=10 tid=0x00007f7b2013a800 nid=0x67fb waiting for monitor entry [0x00007f7b17087000] java.lang.Thread.State: BLOCKED (on object monitor) at com.jiuyan.mountain.test.MyTask.run(App.java:35) - waiting to lock <0x00000007d6b6ddb8> (a java.lang.Integer) at java.lang.Thread.run(Thread.java:745) "t1" prio=10 tid=0x00007f7b20139000 nid=0x67fa waiting on condition [0x00007f7b17188000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method)
t1沒有搶到鎖,所以顯示BLOCKED。t2搶到了鎖,但是處于睡眠中,所以顯示TIMED_WAITING,有限等待某個(gè)條件來喚醒。
把睡眠的代碼去掉,線程狀態(tài)變成了:
"t2" prio=10 tid=0x00007fa0a8102800 nid=0x6a15 waiting for monitor entry [0x00007fa09e37a000] java.lang.Thread.State: BLOCKED (on object monitor) at com.jiuyan.mountain.test.MyTask.run(App.java:35) - waiting to lock <0x0000000784206650> (a java.lang.Integer) at java.lang.Thread.run(Thread.java:745) "t1" prio=10 tid=0x00007fa0a8101000 nid=0x6a14 runnable [0x00007fa09e47b000] java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method)
t1顯示RUNNABLE,說明正在運(yùn)行,這里需要額外說明一下,如果這個(gè)線程正在查詢數(shù)據(jù)庫,但是數(shù)據(jù)庫發(fā)生死鎖,雖然線程顯示在運(yùn)行,實(shí)際上并沒有工作,對于IO型的線程別只用線程狀態(tài)來判斷工作是否正常。
把MyTask的代碼小改一下,線程拿到鎖之后執(zhí)行wait,釋放鎖,進(jìn)入等待區(qū)。
public void run() { synchronized (mutex) { if(mutex == 1) { try { mutex.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
線程狀態(tài)如下:
"t2" prio=10 tid=0x00007fc5a8112800 nid=0x5a58 in Object.wait() [0x00007fc59b58c000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) "t1" prio=10 tid=0x00007fc5a8111000 nid=0x5a57 in Object.wait() [0x00007fc59b68d000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method)
兩個(gè)線程都顯示WAITING,這次是無限期的,需要重新獲得鎖,所以后面跟了on object monitor。
再來個(gè)死鎖的例子:
package com.jiuyan.mountain.test; import java.util.concurrent.TimeUnit; /** * Hello world! * */ public class App { public static void main(String[] args) throws InterruptedException { MyTask task1 = new MyTask(true); MyTask task2 = new MyTask(false); Thread t1 = new Thread(task1); t1.setName("t1"); Thread t2 = new Thread(task2); t2.setName("t2"); t1.start(); t2.start(); } } class MyTask implements Runnable { private boolean flag; public MyTask(boolean flag) { this.flag = flag; } @Override public void run() { if(flag) { synchronized (Mutex.mutex1) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (Mutex.mutex2) { System.out.println("ok"); } } } else { synchronized (Mutex.mutex2) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (Mutex.mutex1) { System.out.println("ok"); } } } } } class Mutex { public static Integer mutex1 = 1; public static Integer mutex2 = 2; }
線程狀態(tài):
"t2" prio=10 tid=0x00007f5f9c122800 nid=0x3874 waiting for monitor entry [0x00007f5f67efd000] java.lang.Thread.State: BLOCKED (on object monitor) at com.jiuyan.mountain.test.MyTask.run(App.java:55) - waiting to lock <0x00000007d6c45bd8> (a java.lang.Integer) - locked <0x00000007d6c45be8> (a java.lang.Integer) at java.lang.Thread.run(Thread.java:745) "t1" prio=10 tid=0x00007f5f9c121000 nid=0x3873 waiting for monitor entry [0x00007f5f67ffe000] java.lang.Thread.State: BLOCKED (on object monitor) at com.jiuyan.mountain.test.MyTask.run(App.java:43) - waiting to lock <0x00000007d6c45be8> (a java.lang.Integer) - locked <0x00000007d6c45bd8> (a java.lang.Integer) at java.lang.Thread.run(Thread.java:745) Found one Java-level deadlock: ============================= "t2": waiting to lock monitor 0x00007f5f780062c8 (object 0x00000007d6c45bd8, a java.lang.Integer), which is held by "t1" "t1": waiting to lock monitor 0x00007f5f78004ed8 (object 0x00000007d6c45be8, a java.lang.Integer), which is held by "t2"
這個(gè)有點(diǎn)像哲學(xué)家就餐問題,每個(gè)線程都持有對方需要的鎖,那就運(yùn)行不下去了。
調(diào)優(yōu)策略兩個(gè)基本原則:
將轉(zhuǎn)移到老年代的對象數(shù)量降到最少。
減少Full GC的執(zhí)行時(shí)間。目標(biāo)是Minor GC時(shí)間在100ms以內(nèi),F(xiàn)ull GC時(shí)間在1s以內(nèi)。
主要調(diào)優(yōu)參數(shù):
設(shè)定堆內(nèi)存大小,這是最基本的。
-Xms:啟動(dòng)JVM時(shí)的堆內(nèi)存空間。
-Xmx:堆內(nèi)存最大限制。
設(shè)定新生代大小。
新生代不宜太小,否則會(huì)有大量對象涌入老年代。
-XX:NewRatio:新生代和老年代的占比。
-XX:NewSize:新生代空間。
-XX:SurvivorRatio:伊甸園空間和幸存者空間的占比。
-XX:MaxTenuringThreshold:對象進(jìn)入老年代的年齡閾值。
設(shè)定垃圾回收器
年輕代:-XX:+UseParNewGC。
老年代:-XX:+UseConcMarkSweepGC。
CMS可以將STW時(shí)間降到最低,但是不對內(nèi)存進(jìn)行壓縮,有可能出現(xiàn)“并行模式失敗”。比如老年代空間還有300MB空間,但是一些10MB的對象無法被順序的存儲(chǔ)。這時(shí)候會(huì)觸發(fā)壓縮處理,但是CMS GC模式下的壓縮處理時(shí)間要比Parallel GC長很多。
G1采用”標(biāo)記-整理“算法,解決了內(nèi)存碎片問題,建立了可預(yù)測的停頓時(shí)間類型,能讓使用者指定在一個(gè)長度為M毫秒的時(shí)間段內(nèi),消耗在垃圾收集上的時(shí)間不得超過N毫秒。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/65568.html
摘要:在本文中我將會(huì)介紹應(yīng)用性能優(yōu)化的一般原則。性能優(yōu)化的流程圖摘取自和合著的性能,描述了應(yīng)用性能優(yōu)化的處理流程。例如,對每臺(tái)服務(wù)器,你面臨著為單個(gè)分配堆內(nèi)存和運(yùn)行個(gè)并為每個(gè)分配堆內(nèi)存的選擇。不過位能使用堆內(nèi)存最大理論值只有。 原文鏈接:http://www.cubrid.org/blog/dev-platform/the-principles-of-java-application-per...
摘要:周四正式發(fā)布了編程語言,將靜態(tài)類型以及一些現(xiàn)代的語言特性引入了。這是對優(yōu)化之路上的新里程碑。但是語言層面的優(yōu)化限制太多,對而言還是不夠用。其次是優(yōu)化運(yùn)行的步驟。在這方面進(jìn)行調(diào)整,可以提升運(yùn)行的性能。值得注意的是,給的影響很大。 Facebook周四正式發(fā)布了Hack編程語言,將靜態(tài)類型以及一些現(xiàn)代的語言特性引入了PHP。這是Facebook對PHP優(yōu)化之路上的新里程碑。 showIm...
摘要:內(nèi)存模型首先介紹下程序具體執(zhí)行的過程源代碼文件后綴會(huì)被編譯器編譯為字節(jié)碼文件后綴由中的類加載器加載各個(gè)類的字節(jié)碼文件,加載完畢之后,交由執(zhí)行引擎執(zhí)行在整個(gè)程序執(zhí)行過程中,會(huì)用一段空間來存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,這段空間一般被 [TOC] JVM內(nèi)存模型 首先介紹下Java程序具體執(zhí)行的過程: Java源代碼文件(.java后綴)會(huì)被Java編譯器編譯為字節(jié)碼文件(....
學(xué)習(xí)JVM的相關(guān)資料 《深入理解Java虛擬機(jī)——JVM高級特性與最佳實(shí)踐(第2版)》 showImg(https://segmentfault.com/img/bVbsqF5?w=200&h=200); 基于最新JDK1.7,圍繞內(nèi)存管理、執(zhí)行子系統(tǒng)、程序編譯與優(yōu)化、高效并發(fā)等核心主題對JVM進(jìn)行全面而深入的分析,深刻揭示JVM的工作原理。以實(shí)踐為導(dǎo)向,通過大量與實(shí)際生產(chǎn)環(huán)境相結(jié)合的案例展示了解...
摘要:高性能代碼的最佳實(shí)踐前言在這篇文章中,我們將討論幾個(gè)有助于提升應(yīng)用程序性能的方法。要獲得有關(guān)應(yīng)用程序需求的最好最可靠的方法是對應(yīng)用程序執(zhí)行實(shí)際的負(fù)載測試,并在運(yùn)行時(shí)跟蹤性能指標(biāo)。 showImg(https://segmentfault.com/img/bVbtgk4?w=256&h=254); 高性能Java代碼的最佳實(shí)踐前言 在這篇文章中,我們將討論幾個(gè)有助于提升Java應(yīng)用程序性...
面試官:今天要不來聊聊JVM調(diào)優(yōu)相關(guān)的吧?面試官:你曾經(jīng)在生產(chǎn)環(huán)境下有過調(diào)優(yōu)JVM的經(jīng)歷嗎?候選者:沒有面試官:...候選者:嗯...是這樣的,我們一般優(yōu)化系統(tǒng)的思路是這樣的候選者:1. 一般來說關(guān)系型數(shù)據(jù)庫是先到瓶頸,首先排查是否為數(shù)據(jù)庫的問題候選者:(這個(gè)過程中就需要評估自己建的索引是否合理、是否需要引入分布式緩存、是否需要分庫分表等等)候選者:2. 然后,我們會(huì)考慮是否需要擴(kuò)容(橫向和縱向都...
閱讀 1244·2023-04-25 18:57
閱讀 2127·2023-04-25 16:28
閱讀 3926·2021-11-24 09:39
閱讀 3631·2021-11-16 11:45
閱讀 1816·2021-10-13 09:40
閱讀 1260·2019-08-30 15:52
閱讀 1715·2019-08-30 10:57
閱讀 657·2019-08-29 16:55