Java性能調優筆記_第1頁
Java性能調優筆記_第2頁
Java性能調優筆記_第3頁
Java性能調優筆記_第4頁
Java性能調優筆記_第5頁
已閱讀5頁,還剩28頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、Java性能調優筆記Java性能調優筆記調優步驟:衡量系統現狀、設定調優目標、尋找性能瓶頸、性能調優、衡量是否到達目標(如果未到達目標,需重新尋找性能瓶頸)、性能調優結束。尋找性能瓶頸性能瓶頸的表象:資源消耗過多、外部處理系統的性能不足、資源消耗不多但程序的響應速度卻仍達不到要求。資源消耗:CPU、文件IO、網絡IO、內存。外部處理系統的性能不足:所調用的其他系統提供的功能或數據庫操作的響應速度不夠。資源消耗不多但程序的響應速度卻仍達不到要求:程序代碼運行效率不夠高、未充分使用資源、程序結構不合理。CPU消耗分析CPU主要用于中斷、內核、用戶進程的任務處理,優先級為中斷內核用戶進程。上下文切換

2、:每個線程分配一定的執行時間,當到達執行時間、線程中有IO阻塞或高優先級線程要執行時,將切換執行的線程。在切換時要存儲目前線程的執行狀態,并恢復要執行的線程的狀態。對于Java應用,典型的是在進行文件IO操作、網絡IO操作、鎖等待、線程Sleep時,當前線程會進入阻塞或休眠狀態,從而觸發上下文切換,上下文切換過多會造成內核占據較多的CPU的使用。運行隊列:每個CPU核都維護一個可運行的線程隊列。系統的load主要由CPU的運行隊列來決定。運行隊列值越大,就意味著線程會要消耗越長的時間才能執行完成。利用率:CPU在用戶進程、內核、中斷處理、IO等待、空閑,這五個部分使用百分比。文件IO消耗分析L

3、inux在操作文件時,將數據放入文件緩存區,直到內存不夠或系統要釋放內存給用戶進程使用。所以通常情況下只有寫文件和第一次讀取文件時會產生真正的文件IO。對于Java應用,造成文件IO消耗高主要是多個線程需要進行大量內容寫入(例如頻繁的日志寫入)的動作、磁盤設備本身的處理速度慢、文件系統慢、操作的文件本身已經很大。網絡IO消耗分析對于分布式Java應用,網卡中斷是不是均衡分配到各CPU(cat/proc/interrupts查看)。內存消耗分析(-Xms和-Xmx設為相同的值,避免運行期JVM堆內存要不斷申請內存)對于Java應用,內存的消耗主要在Java堆內存上,只有創建線程和使用Direct

4、 ByteBuffer才會操作JVM堆外的內存。JVM內存消耗過多會導致GC執行頻繁,CPU消耗增加,應用線程的執行速度嚴重下降,甚至造成OutOfMemoryError,最終導致Java進程退出。JVM堆外的內存swap的消耗、物理內存的消耗、JVM內存的消耗。程序執行慢原因分析鎖競爭激烈:很多線程競爭互斥資源,但資源有限, 造成其他線程都處于等待狀態。未充分使用硬件資源:線程操作被串行化。數據量增長:單表數據量太大(如1個億)造成數據庫讀寫速度大幅下降(操作此表)。調優JVM調優(最關鍵參數為:-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuring

5、Threshold)代大小調優:避免新生代大小設置過小、避免新生代大小設置過大、避免Survivor設置過小或過大、合理設置新生代存活周期。-Xmn 調整新生代大小,新生代越大通常也意味著更多對象會在minor GC階段被回收,但可能有可能造成舊生代大小,造成頻繁觸發Full GC,甚至是OutOfMemoryError。-XX:SurvivorRatio調整Eden區與Survivor區的大小,Eden 區越大通常也意味著minor GC發生頻率越低,但可能有可能造成Survivor區太小,導致對象minor GC后就直接進入舊生代,從而更頻繁觸發Full GC。GC策略的調優:CMS GC

6、多數動作是和應用并發進行的,確實可以減小GC動作給應用造成的暫停時間。對于Web應用非常需要一個對應用造成暫停時間短的GC,再加上Web應用 的瓶頸都不在CPU上,在G1還不夠成熟的情況下,CMS GC是不錯的選擇。(如果系統不是CPU密集型,且從新生代進入舊生代的大部分對象是可以回收的,那么采用CMS GC可以更好地在舊生代滿之前完成對象的回收,更大程度降低Full GC發生的可能) 在調整了內存管理方面的參數后應通過-XX:PrintGCDetails、-XX:+PrintGCTimeStamps、 -XX:+PrintGCApplicationStoppedTime以及jstat或vis

7、ualvm等方式觀察調整后的GC狀況。出內存管理以外的其他方面的調優參數:-XX:CompileThreshold、-XX:+UseFastAccessorMethods、 -XX:+UseBaiasedLocking。程序調優CPU消耗嚴重的解決方法CPU us高的解決方法:CPU us 高的原因主要是執行線程不需要任何掛起動作,且一直執行,導致CPU 沒有機會去調度執行其他的線程。調優方案: 增加Thread.sleep,以釋放CPU 的執行權,降低CPU 的消耗。以損失單次執行性能為代價的,但由于其降低了CPU 的消耗,對于多線程的應用而言,反而提高了總體的平均性能。(在實際的Java應

8、用中類似場景, 對于這種場景最佳方式是改為采用wait/notify機制)對于其他類似循環次數過多、正則、計算等造成CPU us過高的狀況, 則需要結合業務調優。對于GC頻繁,則需要通過JVM調優或程序調優,降低GC的執行次數。 CPU sy高的解決方法:CPU sy 高的原因主要是線程的運行狀態要經常切換,對于這種情況,常見的一種優化方法是減少線程數。調優方案: 將線程數降低這種調優過后有可能會造成CPU us過高,所以合理設置線程數非常關鍵。對于Java分布式應用,還有一種典型現象是應用中有較多的網絡IO操作和確實需要一些鎖競爭機制(如數據庫連接池),但為了能夠支撐搞得并發量,可采用協程(

9、Coroutine)來支撐更高的并發量,避免并發量上漲后造成CPU sy消耗嚴重、系統load迅速上漲和系統性能下降。在Java中實現協程的框架有Kilim,Kilim執行一項任務創建Task,使用Task的暫停機制,而不是Thread,Kilim承擔了線程調度以及上下切換動作,Task相對于原生Thread而言就輕量級多了,且能更好利用CPU。Kilim帶來的是線程使用率的提升,但同時由于要在JVM堆中保存Task上下文信息,因此在采用Kilim的情況下要消耗更多的內存。(目前JDK 7中也有一個支持協程方式的實現,另外基于JVM的Scala的Actor也可用于在Java使用協程)文件IO消

10、耗嚴重的解決方法從程序的角度而言,造成文件IO消耗嚴重的原因主要是多個線程在寫進行大量的數據到同一文件,導致文件很快變得很大,從而寫入速度越來越慢,并造成各線程激烈爭搶文件鎖。常用調優方法:異步寫文件批量讀寫限流限制文件大小網絡IO消耗嚴重的解決方法從程序的角度而言,造成網絡IO消耗嚴重的原因主要是同時需要發送或接收的包太多。常用調優方法:限流,限流通常是限制發送packet的頻率,從而在網絡IO消耗可接受的情況下來發送packget。內存消耗嚴重的解決方法釋放不必要的引用:代碼持有了不需要的對象引用,造成這些對象無法被GC,從而占據了JVM堆內存。(使用ThreadLocal:注意在線程內動

11、作執行完畢時,需執行ThreadLocal.set把對象清除,避免持有不必要的對象引用)使用對象緩存池:創建對象要消耗一定的CPU以及內存,使用對象緩存池一定程度上可降低JVM堆內存的使用。采用合理的緩存失效算法:如果放入太多對象在緩存池中,反而會造成內存的嚴重消耗, 同時由于緩存池一直對這些對象持有引用,從而造成Full GC增多,對于這種狀況要合理控制緩存池的大小,避免緩存池的對象數量無限上漲。(經典的緩存失效算法來清除緩存池中的對象:FIFO、LRU、LFU等)合理使用SoftReference和WeekReference:SoftReference的對象會在內存不夠用的時候回收,Wee

12、kReference的對象會在Full GC的時候回收。資源消耗不多但程序執行慢的情況的解決方法 降低鎖競爭: 多線多了,鎖競爭的狀況會比較明顯,這時候線程很容易處于等待鎖的狀況,從而導致性能下降以及CPU sy上升。使用并發包中的類:大多數采用了lock-free、nonblocking算法。使用Treiber算法:基于CAS以及AtomicReference。使用Michael-Scott非阻塞隊列算法:基于CAS以及AtomicReference,典型ConcurrentLindkedQueue。(基于CAS和AtomicReference來實現無阻塞是不錯的選擇,但值得注意的是,loc

13、k-free算法需不斷的循環比較來保證資源的一致性的,對于沖突較多的應用場景而言,會帶來更高的CPU消耗,因此不一定采用CAS實現無阻塞的就一定比采用lock方式的性能好。 還有一些無阻塞算法的改進:MCAS、WSTM等)盡可能少用鎖:盡可能只對需要控制的資源做加鎖操作(通常沒有必要對整個方法加鎖,盡可能讓鎖最小化,只對互斥及原子操作的地方加鎖,加鎖時盡可能以保護資源的最小化粒度為單位-如只對需要保護的資源加鎖而不是this)。拆分鎖:獨占鎖拆分為多把鎖(讀寫鎖拆分、類似ConcurrentHashMap中默認拆分為16把鎖),很多程度上能提高讀寫的性能,但需要注意在采用拆分鎖后,全局性質的操

14、作會變得比較復雜(如ConcurrentHashMap中size操作)。(拆分鎖太多也會造成副作用,如CPU消耗明顯增加)去除讀寫操作的互斥:在修改時加鎖,并復制對象進行修改,修改完畢后切換對象的引用,從而讀取時則不加鎖。這種稱為CopyOnWrite,CopyOnWriteArrayList是典型實現,好處是可以明顯提升讀的性能,適合讀多寫少的場景, 但由于寫操作每次都要復制一份對象,會消耗更多的內存。充分利用硬件資源(CPU和內存): 充分利用CPU在能并行處理的場景中未使用足夠的線程(線程增加:CPU資源消耗可接受且不會帶來激烈競爭鎖的場景下), 例如單線程的計算,可以拆分為多個線程分別

15、計算,最后將結果合并,JDK 7中的fork-join框架。Amdahl定律公式:1/(F+(1-F)/N)。 充分利用內存數據的緩存、耗時資源的緩存(數據庫連接創建、網絡連接的創建等)、頁面片段的緩存。畢竟內存的讀取肯定遠快于硬盤、網絡的讀取, 在內存消耗可接受、GC頻率、以及系統結構(例如集群環境可能會帶來緩存的同步)可接受情況下,應充分利用內存來緩存數據,提升系統的性能。總結:好的調優策略是收益比(調優后提升的效果/調優改動所需付出的代價)最高的,通常來說簡單的系統調優比較好做,因此盡量保持單機上應用的純粹性, 這是大型系統的基本架構原則。調優的三大有效原則:充分而不過分使用硬件資源、合

16、理調整JVM、合理使用JDK包。 學習參考資料:分布式Java應用:基礎與實踐 補充分布式Java應用:基礎與實踐一些代碼樣例:cpu- CpuNotUseEffectiveDemojava view plain copy/* * */ package gram.cpu; import java.util.ArrayList; import java.util.List; import java.util.Random; /* * 未充分利用CPU:在能并行處理的場景中未使用足夠的線程(線程增加:CPU資源消耗可接受且不會帶來激烈競爭鎖的場景下) * * author yang

17、wm Aug 25, 2010 9:54:50 AM */ public class CpuNotUseEffectiveDemo private static int executeTimes = 10; private static int taskCount = 200; public static void main(String args) throws Exception Task task = new Task(); for (int i = 0; i taskCount; i+) task.addTask(Integer.toString(i); long beginTime

18、= System.currentTimeMillis(); for (int i = 0; i executeTimes; i+) System.out.println(Round: + (i + 1); Thread thread = new Thread(task); thread.start(); thread.join(); long endTime = System.currentTimeMillis(); System.out.println(Execute summary: Round( + executeTimes + ) TaskCount Per Round( + task

19、Count + ) Execute Time ( + (endTime - beginTime) + ) ms); static class Task implements Runnable List tasks = new ArrayList(); Random random = new Random(); boolean exitFlag = false; public void addTask(String task) List copyTasks = new ArrayList(tasks); copyTasks.add(task); tasks = copyTasks; Overri

20、de public void run() List runTasks = tasks; List removeTasks = new ArrayList(); for (String task : runTasks) try Thread.sleep(random.nextInt(10); catch (Exception e) e.printStackTrace(); removeTasks.add(task); try Thread.sleep(10); catch (Exception e) e.printStackTrace(); /* Round: 1 . Round: 10 Exe

21、cute summary: Round( 10 ) TaskCount Per Round( 200 ) Execute Time ( 10687 ) ms */ CpuUseEffectiveDemojava view plain copy/* * */ package gram.cpu; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; /* * 充分利用CPU:在能并行處理的場景中使用

22、足夠的線程(線程增加:CPU資源消耗可接受且不會帶來激烈競爭鎖的場景下) * * author yangwm Aug 25, 2010 9:54:50 AM */ public class CpuUseEffectiveDemo private static int executeTimes = 10; private static int taskCount = 200; private static final int TASK_THREADCOUNT = 16; private static CountDownLatch latch; public static void main(St

23、ring args) throws Exception Task tasks = new TaskTASK_THREADCOUNT; for (int i = 0; i TASK_THREADCOUNT; i+) tasksi = new Task(); for (int i = 0; i taskCount; i+) int mod = i % TASK_THREADCOUNT; tasksmod.addTask(Integer.toString(i); long beginTime = System.currentTimeMillis(); for (int i = 0; i execut

24、eTimes; i+) System.out.println(Round: + (i + 1); latch = new CountDownLatch(TASK_THREADCOUNT); for (int j = 0; j TASK_THREADCOUNT; j+) Thread thread = new Thread(tasksj); thread.start(); latch.await(); long endTime = System.currentTimeMillis(); System.out.println(Execute summary: Round( + executeTim

25、es + ) TaskCount Per Round( + taskCount + ) Execute Time ( + (endTime - beginTime) + ) ms); static class Task implements Runnable List tasks = new ArrayList(); Random random = new Random(); boolean exitFlag = false; public void addTask(String task) List copyTasks = new ArrayList(tasks); copyTasks.ad

26、d(task); tasks = copyTasks; Override public void run() List runTasks = tasks; List removeTasks = new ArrayList(); for (String task : runTasks) try Thread.sleep(random.nextInt(10); catch (Exception e) e.printStackTrace(); removeTasks.add(task); try Thread.sleep(10); catch (Exception e) e.printStackTr

27、ace(); latch.countDown(); /* Round: 1 . Round: 10 Execute summary: Round( 10 ) TaskCount Per Round( 200 ) Execute Time ( 938 ) ms */ fileio- IOWaitHighDemojava view plain copy/* * */ package gram.fileio; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import ja

28、va.util.Random; /* * 文件IO消耗嚴重的原因主要是多個線程在寫進行大量的數據到同一文件, * 導致文件很快變得很大,從而寫入速度越來越慢,并造成各線程激烈爭搶文件鎖。 * * author yangwm Aug 21, 2010 9:48:34 PM */ public class IOWaitHighDemo private String fileName = iowait.log; private static int threadCount = Runtime.getRuntime().availableProcessors(); private Random ran

29、dom = new Random(); public static void main(String args) throws Exception if (args.length = 1) threadCount = Integer.parseInt(args1); IOWaitHighDemo demo = new IOWaitHighDemo(); demo.runTest(); private void runTest() throws Exception File file = new File(fileName); file.createNewFile(); for (int i =

30、 0; i threadCount; i+) new Thread(new Task().start(); class Task implements Runnable Override public void run() while (true) try StringBuilder strBuilder = new StringBuilder(=begin=/n); String threadName = Thread.currentThread().getName(); for (int i = 0; i jstack 2656 2010-08-21 23:24:17 Full threa

31、d dump Java HotSpot(TM) Client VM (17.0-b05 mixed mode): DestroyJavaVM prio=6 tid=0x00868c00 nid=0xde0 waiting on condition 0x00000000 java.lang.Thread.State: RUNNABLE Thread-1 prio=6 tid=0x0ab9dc00 nid=0xb7c runnable 0x0b0bf000 java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.close0(Nat

32、ive Method) at java.io.FileOutputStream.close(FileOutputStream.java:336) at sun.nio.cs.StreamEncoder.implClose(StreamEncoder.java:320) at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:149) - locked (a java.io.FileWriter) at java.io.OutputStreamWriter.close(OutputStreamWriter.java:233) at java.io

33、.BufferedWriter.close(BufferedWriter.java:265) - locked (a java.io.FileWriter) at tune.IOWaitHighDemo$Task.run(IOWaitHighDemo.java:58) at java.lang.Thread.run(Thread.java:717) Thread-0 prio=6 tid=0x0ab9d400 nid=0x80c runnable 0x0b06f000 java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.wr

34、iteBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:292) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:282) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125) - locked (a java.io.FileWriter)

35、at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207) at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:128) - locked (a java.io.FileWriter) at java.io.BufferedWriter.write(BufferedWriter.java:229) - locked (a java.io.FileWriter) at java.io.Writer.write(Writer.java:157) at tune.IO

36、WaitHighDemo$Task.run(IOWaitHighDemo.java:57) at java.lang.Thread.run(Thread.java:717) Low Memory Detector daemon prio=6 tid=0x0ab6f800 nid=0xfb0 runnable 0x00000000 java.lang.Thread.State: RUNNABLE CompilerThread0 daemon prio=10 tid=0x0ab6c800 nid=0x5fc waiting on condition 0x00000000 java.lang.Thr

37、ead.State: RUNNABLE Attach Listener daemon prio=10 tid=0x0ab67800 nid=0x6fc waiting on condition 0x00000000 java.lang.Thread.State: RUNNABLE Signal Dispatcher daemon prio=10 tid=0x0ab66800 nid=0x5a0 runnable 0x00000000 java.lang.Thread.State: RUNNABLE Finalizer daemon prio=8 tid=0x0ab54000 nid=0xe74

38、 in Object.wait() 0x0ac8f000 java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) - locked (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.

39、ReferenceQueue.remove(ReferenceQueue.java:151) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177) Reference Handler daemon prio=10 tid=0x0ab4f800 nid=0x8a4 in Object.wait() 0x0ac3f000 java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waitin

40、g on (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) - locked (a java.lang.ref.Reference$Lock) VM Thread prio=10 tid=0x0ab4a800 nid=0x1d0 runnable VM Periodic Task Thread prio=10 tid=0x0ab7d400 nid=0x464 waiting on condition JNI global references: 693 C:/Documents and Settings/yangwm */ LogControljava view plain copy/* * */ package gram.fileio; import java.util.concurrent.atomic.AtomicInteger; /* * 日志控制:采用簡單策略為統計一段時間內日志輸出頻率, 當超出這個頻率時,一段時間內不再寫log * * author yangwm Aug 24, 2

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論