改善性能意味着用更少的資源作更多的事情。爲了利用併發來提升系統性能,咱們須要更有效的利用現有的處理器資源,這意味着咱們指望使 CPU 儘量出於忙碌狀態(固然,並非讓 CPU 週期出於應付無用計算,而是讓 CPU 作有用的事情而忙)。若是程序受限於當前的 CPU 計算能力,那麼咱們經過增長更多的處理器或者經過集羣就能提升總的性能。總的來講,性能提升,須要且僅須要解決當前的受限資源,當前受限資源多是:java
若是你的系統有以下的特色,說明系統存在性能瓶頸:算法
隨着系統逐步增長壓力,CPU 使用率沒法趨近 100%(以下圖)數據庫
持續運行緩慢。時常發現應用程序運行緩慢。經過改變環境因子(負載,鏈接數等)也沒法有效提高總體響應時間網絡
一個好的程序,應該是可以充分利用 CPU 的。若是一個程序在單 CPU 的機器上不管多大壓力都不能使 CPU 使用率接近 100%,說明這個程序設計有問題。一個系統的性能瓶頸分析過程大體以下:多線程
高性能在不一樣的應用場合下,有不一樣的含義:併發
性能調優的終極目標是:系統的 CPU 利用率接近 100%,若是 CPU 沒有被充分利用,那麼有以下幾個可能:oracle
下面是一種常見的錯誤app
兩個不相干的方法(沒有使用同一個共享變量),共用了 this 鎖,致使人爲的資源競爭上面的代碼將 synchronized 加在類的每個方法上面,違背了保護什麼鎖什麼的原則。對於無共享資源的方法,使用了同一個鎖,人爲形成了沒必要要的等待。Java 缺省提供了 this 鎖,這樣不少人喜歡直接在方法上使用 synchronized 加鎖,不少狀況下這樣作是不恰當的,若是不考慮清楚就這樣作,很容易形成鎖粒度過大:socket
上面的代碼應該變成下面數據庫設計
單 CPU 場合 將耗時操做拿到同步塊以外,有的狀況下能夠提高性能,有的場合則不能:上面的代碼,會致使一個線程長時間佔有鎖,而在這麼長的時間裏其餘線程只能等待,這種寫法在不一樣的場合下有不一樣的提高餘地:
無論如何,縮小同步範圍,對系統沒有任何很差的影響,大多數狀況下,會帶來性能的提高,因此必定要縮小同步範圍,所以上面的代碼應該改成
上面提到的這些緣由造成的性能瓶頸,均可以經過線程堆棧分析,找到根本緣由。
性能瓶頸的幾個特徵:
鑑於性能瓶頸的以上特色,進行性能模擬的時候,必定要使用比系統當前稍高的壓力下進行模擬,不然性能瓶頸不會出現。具體步驟以下:
經過線程堆棧,能夠很容易的識別多線程場合下高負載的時候纔會出現的性能瓶頸。一旦一個系統出現性能瓶頸,最重要的就是識別性能瓶頸,而後根據識別的性能瓶頸進行修改。通常多線程系統,先按照線程的功能進行歸類(組),把執行相同功能代碼的線程做爲一組進行分析。當使用堆棧進行分析的時候,以這一組線程進行統計學分析。若是一個線程池爲不一樣的功能代碼服務,那麼將整個線程池的線程做爲一組進行分析便可。
通常一個系統一旦出現性能瓶頸,從堆棧上分析,有以下三種最爲典型的堆棧特徵:
一個例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
"Thread-243" prio=1 tid=0xa58f2048 nid=0x7ac2 runnable [0xaeedb000..0xaeedc480] at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.read(SocketInputStream.java:129) at oracle.net.ns.Packet.receive(Unknown Source) ... ... at oracle.jdbc.driver.LongRawAccessor.getBytes() at oracle.jdbc.driver.OracleResultSetImpl.getBytes() - locked <0x9350b0d8> (a oracle.jdbc.driver.OracleResultSetImpl) at oracle.jdbc.driver.OracleResultSet.getBytes(O) ... ... at org.hibernate.loader.hql.QueryLoader.list() at org.hibernate.hql.ast.QueryTranslatorImpl.list() ... ... at com.wes.NodeTimerOut.execute(NodeTimerOut.java:175) at com.wes.timer.TimerTaskImpl.executeAll(TimerTaskImpl.java:707) at com.wes.timer.TimerTaskImpl.execute(TimerTaskImpl.java:627) - locked <0x80df8ce8> (a com.wes.timer.TimerTaskImpl) at com.wes.threadpool.RunnableWrapper.run(RunnableWrapper.java:209) at com.wes.threadpool.PooledExecutorEx$Worker.run() at java.lang.Thread.run(Thread.java:595) "Thread-248" prio=1 tid=0xa58f2048 nid=0x7ac2 runnable [0xaeedb000..0xaeedc480] at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.read(SocketInputStream.java:129) at oracle.net.ns.Packet.receive(Unknown Source) ... ... at oracle.jdbc.driver.LongRawAccessor.getBytes() at oracle.jdbc.driver.OracleResultSetImpl.getBytes() - locked <0x9350b0d8> (a oracle.jdbc.driver.OracleResultSetImpl) at oracle.jdbc.driver.OracleResultSet.getBytes(O) ... ... at org.hibernate.loader.hql.QueryLoader.list() at org.hibernate.hql.ast.QueryTranslatorImpl.list() ... ... at com.wes.NodeTimerOut.execute(NodeTimerOut.java:175) at com.wes.timer.TimerTaskImpl.executeAll(TimerTaskImpl.java:707) at com.wes.timer.TimerTaskImpl.execute(TimerTaskImpl.java:627) - locked <0x80df8ce8> (a com.wes.timer.TimerTaskImpl) at com.wes.threadpool.RunnableWrapper.run(RunnableWrapper.java:209) at com.wes.threadpool.PooledExecutorEx$Worker.run() at java.lang.Thread.run(Thread.java:595) ... ... "Thread-238" prio=1 tid=0xa4a84a58 nid=0x7abd in Object.wait() [0xaec56000..0xaec57700] at java.lang.Object.wait(Native Method) at com.wes.collection.SimpleLinkedList.poll(SimpleLinkedList.java:104) - locked <0x6ae67be0> (a com.wes.collection.SimpleLinkedList) at com.wes.XADataSourceImpl.getConnection_internal(XADataSourceImpl.java:1642) ... ... at org.hibernate.impl.SessionImpl.list() at org.hibernate.impl.SessionImpl.find() at com.wes.DBSessionMediatorImpl.find() at com.wes.ResourceDBInteractorImpl.getCallBackObj() at com.wes.NodeTimerOut.execute(NodeTimerOut.java:152) at com.wes.timer.TimerTaskImpl.executeAll() at com.wes.timer.TimerTaskImpl.execute(TimerTaskImpl.java:627) - locked <0x80e08c00> (a com.facilities.timer.TimerTaskImpl) at com.wes.threadpool.RunnableWrapper.run(RunnableWrapper.java:209) at com.wes.threadpool.PooledExecutorEx$Worker.run() at java.lang.Thread.run(Thread.java:595)
"Thread-233" prio=1 tid=0xa4a84a58 nid=0x7abd in Object.wait() [0xaec56000..0xaec57700]
at java.lang.Object.wait(Native Method) at com.wes.collection.SimpleLinkedList.poll(SimpleLinkedList.java:104) - locked <0x6ae67be0> (a com.wes.collection.SimpleLinkedList) at com.wes.XADataSourceImpl.getConnection_internal(XADataSourceImpl.java:1642) ... ... at org.hibernate.impl.SessionImpl.list() at org.hibernate.impl.SessionImpl.find() at com.wes.DBSessionMediatorImpl.find() at com.wes.ResourceDBInteractorImpl.getCallBackObj() at com.wes.NodeTimerOut.execute(NodeTimerOut.java:152) at com.wes.timer.TimerTaskImpl.executeAll() at com.wes.timer.TimerTaskImpl.execute(TimerTaskImpl.java:627) - locked <0x80e08c00> (a com.facilities.timer.TimerTaskImpl) at com.wes.threadpool.RunnableWrapper.run(RunnableWrapper.java:209) at com.wes.threadpool.PooledExecutorEx$Worker.run() at java.lang.Thread.run(Thread.java:595) ... ... |
從堆棧看,有 51 個(socket)訪問,其中有 50 個是 JDBC 數據庫訪問。其餘方法被阻塞在 java.lang.Object.wait() 方法上。
減小鎖的粒度,好比 ConcurrentHashMap 的實現默認使用 16 個鎖的 Array(有一個反作用:鎖整個容器會很費力,能夠添加一個全局鎖)
性能調優總有一個終止條件,若是系統知足以下兩個條件,便可終止: