--From : JAVA程式性能優化 (葛一鳴,清華大學出版社,2012/10第一版) 1. java性能調優概述 1.1 性能概述 程式性能: 執行速度,記憶體分配,啟動時間, 負載承受能力。 性能指標: 執行時間,CPU時間,記憶體分配,磁碟吞吐量,網路吞吐量,響應時間。 優化策略: 木桶原理,優 ...
--From : JAVA程式性能優化 (葛一鳴,清華大學出版社,2012/10第一版)
1. java性能調優概述
1.1 性能概述
程式性能: 執行速度,記憶體分配,啟動時間, 負載承受能力。
性能指標: 執行時間,CPU時間,記憶體分配,磁碟吞吐量,網路吞吐量,響應時間。
優化策略: 木桶原理,優化性能瓶頸。
1.2 性能調優的層次
設計調優,
代碼調優,
JVM調優,
資料庫調優,
操作系統調優。
2. 設計優化
2.1 善用設計模式
單例模式: 對於巨大對象,節省創建對象的時間空間;
代理模式: 可用於延遲載入,提升系統啟動速度;
享元模式: 對象和組件復用,節省創建對象的開銷;
裝飾者模式: 分離組件,提高可維護性增加模塊復用性;
觀察者模式: 避免新開線程迴圈等待;
2.2 常用組件優化
緩衝(漏斗設計),
緩存(空間換時間),
對象復用“池”,
並行代替串列,
負載均衡(Terracotta),
時間換空間,
空間換時間。
3. Java程式優化
3.1 字元串
不變性, 針對常量池優化
subString 空間換取時間
split 正則分割
StringTokenizer 字元串分割
靈活使用charAt 和 indexOf
StringBuffer(線程安全) 和 StringBuilder : 初始化時設置最大容量
3.2 核心數據結構
List : ArrayList 和 LinkedList
Map : Hashtable(線程安全), HashMap, LinkedHashMap, TreeMap(可排序)
Set : hashSet, linkedHashSet, TreeSet
集合訪問優化 :
分離迴圈中被重覆調用的代碼 ( i<collection.size() -> i<collectionSize )
省略相同的操作 (迴圈內部的聲明)
減少方法調用 (優先直接訪問內部變數)
RandomAccess (隨即訪問): ArrayList Vector
3.3 使用NIO提升性能
NIO的Buffer 和 Channel 配合使用
Buffer的原理和相關操作
Buffer記憶體緩存和堆上記憶體區別
3.4 引用類型
強引用 : 可以直接訪問,不會被GC回收,可能導致記憶體泄露(互相引用)
軟引用 : SoftReference<T> 記憶體緊張,會被GC回收
弱引用 : WeakReference<T> GC發現立即回收
虛引用 : PhantomReference<T> 始終返回null
WeakHashMap : 弱引用的 HashMap
3.5 改善性能
慎用異常,try catch浪費性能 (62ms/110ms)
使用局部變數,棧的訪問比堆快 (78ms/266ms)
位運算代替乘除法 (31ms/219ms)
一維數組代替二維數組 (235ms/344ms)
提取公共表達式
降低迴圈次數 如數組賦值 i++ -> i+=3 (31ms/94ms)
布爾運算代替位運算 (邏輯運算中布爾運算快於位運算,越簡單的越靠前)
使用System.arrayCopy()(native 函數)複製數組(31ms/250ms)
Buffer進行I/O操作
使用clone()代替new (一般是淺拷貝,其他需要具體實現clone)
靜態方法代替實例方法
4. 並行程式開發及優化
4.1 並行程式設計模式
Future 模式 : 提高響應速度
Master_worker 模式 : 子任務分配,提高響應速度
Guarded Suspension 模式 : 隊列緩衝,非立即處理,避免因為請求太多而崩潰
不變模式 : 類似String/Double...不需要同步,線程安全
生產者消費者模式 : 緩解兩者間的性能差
4.2 JDK多任務執行框架
無限制線程的缺陷
簡單的線程池
Exector 框架
自定義線程池 : ThreadPoolExecutor
優化線程池大小 : n = Ncpu * Ucpu *(1 +W/C)
擴展 ThreadPoolExector
4.3 JDK併發數據結構
併發 list : CopyOnWriteArrayList
併發 Set : CopyOnWriteArraySet
併發 Map : ConcurrentHashMap
併發 Queue : ConcurrentLinkedQueue (使用CAS無鎖) / LinkedBlockingQueue
併發 Deque : LinkedBlockingDeque (讀寫都加鎖)
4.4 併發控制方法
Java記憶體模型與 volatile
同步關鍵字 synchronized
ReentrantLock 重入鎖
ReentrantReadWriteLock 讀寫鎖 (並讀串寫)
Condition 對象
Semaphore 信號量 (限制最大訪問線程數)
ThreadLocal 線程局部變數
4.5 鎖的性能和優化
線程的開銷
避免死鎖
減小鎖持有時間
減小鎖粒度
讀寫分離鎖來替換獨占鎖
鎖分離 (LinkedBlockingQueue)
重入鎖 (ReentrantLock) 和內部鎖(Synchronized)
鎖粗化 Lock Coarsening (多次重覆請求消耗資源)
自旋鎖 Spinning Lock (執行空迴圈來減少被掛起的次數)
鎖消除 Lock Elimination (消除不必要的鎖)
鎖偏向 Biased Lock (高併發效率低)
4.6 無鎖的並行計算
非阻塞的同步/無鎖 (基於CAS)
原子操作 (java.util.concurrent.atomic)
Amino 框架 : 提供現成安全的,基於無鎖的數據結構,內置了多線程調度模式。
Amino 集合 : LockFreeList(linked)/LockFreeVector(ArrayList); LockFreeSet
Amino 樹 : LockFreeBSTree(二叉搜索樹)
Amino 圖 : UndirectedGraph<E> / DirectedGraph<E>
Amino 簡單調度模式 : Master_worker (靜態/動態)
4.7 協程
協程的概念 : 進程的分割式線程,線程的分割是協程
Kilim 框架 : 當需要大量線程時,使用協程能提高性能,降低線程維護切換的性能
5. JVM調優
5.1 Java 虛擬機記憶體模型
程式計數器 : 線程私有的記憶體空間,互不影響; 當執行java方法,程式計數器為Java位元組碼地址,當執行native方法程式計數器為空
Java虛擬機棧 : 線程私有的記憶體空間, 用於管理Java函數調用, -Xss1M
本地方法棧 : 管理本地方法調用
Java堆 : 分為新生代和老年代兩部分
方法區 : 和Java堆一樣被所有線程共用;包括類的類型信息,常量池,域信息,方法信息,也叫永久區
5.2 JVM 記憶體分配參數
-Xmx : 設置最大堆記憶體
-Xms : 設置初始堆記憶體
-Xss : 設置線程棧 (使用系統記憶體,堆空間分配越大,線程總數越少) JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K
-Xmn : 設置新生代 (一般為堆空間的1/4~1/3 )
-XX:NewSize : 設置初始新生代空間
-XX:MaxNewSize : 設置最大新生代空間
-XX:PermSize : 設置初始持久區
-XX:MaxPermSize : 設置最大持久區
-XX:SurvivorRatio 設置新生代比例 (eden : survivor)
-XX:NewRatio : 設置heap比例 (老年代 : 新生代)
-XX:TargetSurvivorRatio : 設置suvivior使用率,超過空間會送入老年代
5.3 垃圾收集基礎
垃圾收集的作用
垃圾收集演算法與思想
1) 引用計演算法 : 記錄對象引用數,但兩個對象的迴圈引用會導致記憶體泄露
2) 標記清除法 : 記錄根節點開始的可達對象,但回收後造成空間碎片
3) 複製演算法 : 在存活對象少垃圾多的情況下,系統空間折半,複製有效對象到另一空間,適用於新生代對象(from space to space)
4) 標記壓縮演算法 : 在存活對象多垃圾少的情況下,清除無效對象,壓縮有效對象,清除空間碎片,適用於老年代
5) 增量演算法 : 避免長時間占據應用程式執行時間,而交替執行,每次清理一小片記憶體空間,但由於線程頻繁上下文切換,會降低系統吞吐量
6) 分代 : 分為年輕代和老年代,分別使用不同的垃圾收集演算法,新生代使用複製演算法,老年代使用標記壓縮演算法
垃圾收集器的類型
線程數 : 串列/並行
工作模式 : 併發/獨占
碎片處理 : 壓縮/非壓縮
分代 : 新生代/老年代
評價GC策略的指標 : 吞吐量/垃圾回收器負載/停頓時間/垃圾回收頻率/反應時間/堆分配
新生代串列收集器 : 單線程,獨占
老年代串列收集器 : 標記壓縮,串列獨占
並行收集器 : 多線程,獨占
新生代並行回收收集器 : 多線程,獨占,關註系統吞吐量
老年代並行回收收集器 : 標記壓縮,關註系統吞吐量
CMS(concurrent Mark Sweep)收集器 : 標記清除法,多線程並行,非獨占
G1(Garbage First)收集器 : 關註吞吐量和停頓控制,適用標記壓縮法
Stop the World : 整個應用停止等待垃圾回收完成
收集器對系統性能的影響
GC相關參數總結
1) 串列回收器相關參數
-XX:+UseSerialGC : 新生代串列收集器和老年代串列收集器
-XX:SurvivorRatio : 設置eden和survivor的比例
-XX:PretenureSizeThreshold : 設置大對象進入老年代的閾值,對象大小超過此值,直接在老年代分配,只對串列收集器和新生代並行收集器有效,並行回收收集器不識別此參數
-XX:MaxTenuringThreshold : 設置進入老年代的年齡最大值,每次Minor gc後,對象年齡+1, 預設15
2) 並行回收器相關參數
-XX:+UseParNewGC : 新生代使用並行收集器
-XX:+UseParallelOldGC : 老年代使用並行回收收集器
-XX:ParallelGCThreads : 垃圾回收線程數
-XX:MaxGCPauseMillis : 最大垃圾收集停頓時間
-XX:GCTimeRatio : 設置吞吐量大小n, GCTime < 1/(1+n)
-XX:+UseAdaptiveSizePolicy : 打開自適應GC策略,新生代大小,eden和survivor比例等自動調整
3) 與CMS回收器相關參數
-XX:+UseConcMarkSweepGC : 新生代使用並行收集器,老年代使用CMS+串列收集器
-XX:parallelCMSThreads : CMS的線程數量
-XX:CMSInitiatingOccupancyFraction : 設置CMS收集器在老年代空間使用多少後觸發,預設68%
-XX:+UseCMSCompactAtFullCollection : 設置CMS收集器在完成垃圾收集後是否進行一次記憶體碎片整理
-XX:CMSFullGCsBeforeCompation : 設置多少次CMS垃圾回收後進行記憶體壓縮
-XX:+CMSClassUnloadingEnabled : 允許對類元數據進行回收
-XX:+CMSParallelRemarkEnabled : 啟用並行標記
-XX:CMSInitiatingPermOccupancyFraction : 永久區達到此閾值,啟動CMS回收(前提是 -XX:CMSClassUnloadingEnabled激活)
-XX:UseCMSInitiatingOccupancyOnly : 達到閾值才進行CMS回收
-XX:+CMSIncrementalMode : 使用增量模式,適合單CPU
4) 與G1回收器相關參數
-XX:+UseG1GC : 使用G1回收器
-XX:+UnlockExperimentalVMOptions : 允許使用實驗性參數
-XX:MaxGCPauseMillis : 設置最大垃圾停頓時間
-XX:GCPauseIntervalMillis : 設置垃圾收集間隔時間
5) 其他參數
-XX:+DisableExplicitGC : 禁用顯示GC
5.4 常用調優案例和方法
1) 將新對象預留在新生代 : 新生代的垃圾回收速度高於老年代回收,可設置新生代大小或者新生代和老年代比例調整新生代大小
2) 大對象進入老年代 : 大對象需要空間,可能導致空間不足或者需要移動大量小對象到老年代,設置-XX:PretenureSizeThreshold
3) 設置對象進入老年代的年齡 : gc一次年齡+1,年齡到達閾值進入老年代,設置閾值 -XX:MaxTenuringThreshold
4) 穩定與震蕩的堆大小 : 空間大GC速度慢,空間小GC速度快,大部分設置最大最小相同
-XX:MinHeapFreeRatio : 設置堆空間最小空閑比例,預設40;
-XX:MaxHeapFreeRatio : 設置對空間最大空閑比例,預設70;
5) 吞吐量優先案例 : java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
6) 使用大頁案例 : java -Xmx2506m -Xms2506 -Xmn1536m -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:LargePageSizeInBytes=256m (設置大頁大小)
7) 降低停頓案例 : java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC _XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
5.5 實用JVM參數
1) JIT(Just-In-Time)編譯參數 : 運行時,函數運行次數超過閾值,將位元組碼編譯成本地代碼提高執行效率,
-XX:CompileThreshold : 編譯閾值
-XX:+PrintCompilation : 列印JIT編譯信息
-XX:+CITime 列印JIT編譯的基本信息
2) 堆快照(堆Dump) : 發生OOM時導出Dump文件,可以使用Visual VM等工具分析
-XX:+HeapDumpOnOutOfMemoryError : 發生OOM時導出當前堆快照
-XX:HeapDumpPath : 指定堆快照信息保存文件位置
3) 錯誤處理 : 發生OOM時運行第三方腳本
-XX:OnOutOfMemoryError=c:\reset.bat
4) 取得GC信息
-verbose:gc
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps : 列印每次GC的發生時間
-XX:+PrintTenuringDistribution : 察看新生對象晉升老年代的實際閾值
-XX:+PrintHeapAtGC : 每次GC列印堆的使用情況
-XX:+PrintGCApplicationStoppedTime : 顯示應用程式在GC發生時的停頓時間
-XX:+PrintGCApplicationConcurrentTime : 應用程式在GC停頓時間的執行時間
-Xloggc:C:\gc.log : GC日誌
5) 類和對象跟蹤
-XX:+TraceClassLoading : 跟蹤類載入情況
-XX:+TraceClassUnloading : 跟蹤類的卸載情況
-verbose:class : 跟蹤類的載入和卸載情況
-XX:+PrintClassHistogram : 當按下Ctrl + Break,輸出系統內類的統計信息
6) 控制GC
-XX:DisableExplicitGC : 禁止顯示GC操作,避免程式員大量使用System.gc()降低程式性能
-Xnoclassgc : 禁止回收類(class,而不是對象實例)
7) 選擇類校驗器
-XX:-UseSplitVerifier :實用舊的類校驗器,JDK1.6預設開啟新的類校驗器
-XX:-FailOverToOldVerifier : 關閉再次校驗功能, 預設新的校驗器失敗會再次使用老的校驗器
8) Solaris下線程式控制制
-XX:+UseBoundThreads : 綁定所有用戶線程到內核線程,減少線程進入饑餓狀態的次數
-XX:+UseLWPSychronization : 使用內核線程代替線程同步
-XX:+UseVMInterruptibleIO : 允許運行時中斷線程
9) 使用大頁
-XX:+UseLargePages : 啟用大頁
-XX:LargePageSizeInBytes : 指定大頁的大小
10) 壓縮指針
-XX:+UseCompressedOops : 打開指針壓縮,壓縮class的屬性指針(靜態成員),對象的屬性指針,普通對象的每個元素指針, 能節省記憶體,但是會消耗一定的性能
5.6 實戰JVM調優
1) Tomcat簡介與啟動加速 : 減少GC次數,擴大新生代,禁止顯示調用GC
2) 一個示例web : 每個用戶分配1m空間
3) JMEter介紹和使用 : 基於java的性能測試和壓力測試工具,生成包括響應時間,錯誤數和吞吐量的報告
4) 調優前Web應用運行狀況 : GC過多,吞吐量為38.7/s
5) 調優過程 : 確定堆記憶體大小,合理分配新生代和老年代,確定永久區大小,選擇垃圾收集器,對垃圾收集器設置,禁用顯示gc,進用類元數據回收,禁用類驗證等
設置堆空間和永久區,禁用顯示gc,去掉類驗證,吞吐量為47.9/s
-Xmx512M(-Xmx256M)
-Xms512M
-XX:permSize=32M
-XX:MaxPermSize=32M
-XX:+DisableExplicitGC
-Xverify:none
使用並行回收收集器,吞吐量為51/s
-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:ParallelGCThreads=8
6 Java性能調優工具
6.1 Linux命令行工具
1) top : 顯示系統中各個進程的資源占用狀況
2) sar : 周期性對記憶體,I/O和CPU情況採樣
3) vmstat : 統計CPU記憶體使用情況,swap使用情況,可指定周期性採樣和次數
4) iostat : 提供詳盡的I/O信息,可指定周期性採樣和次數
6) pidstat : 監測進程和線程的情況,
監測CPU : 可以配合 jstack -l $ThreadId 察看進程堆棧
I/O使用監控
記憶體監控
6.2 Windows工具
1) 任務管理器
2) perfmon性能檢測工具 : 可以針對一個進程進行監控
3) Process Explorer : 需安裝
4) pslist : 需安裝
6.3 JDK命令行工具
1) jps : 類似Linux下的ps,用於列出Java進程,-m(主函數的參數),-l(主函數路徑),-v(顯示JVM參數)
2) jstat : 觀察Java應用程式運行時的信息
3) jinfo : 察看Java應用程式的擴展參數(JVM參數),可通過此命令來查看預設參數值,可修改參數
4) jmap : 生成堆快照和對象的統計信息
5) jhat : 分析Java應用程式的對快照內容,可在瀏覽器中訪問http://127.0.0.1:7000
6) jstack : 導出Java應用車功能需的線程堆棧
7) jstatd : RMI的服務端程式,建立遠程監控通信,需要使用java的安全策略在使用
8) hprof : 非獨立監控工具,用於監測Java程式的CPUh和堆信息,監控函數運行時間,導出程式堆內容
6.4 JConsole工具 : %javahome%/bin/jconsole.exe
1) JConsole連接Java程式 : 添加啟動參數可遠程連接
-Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=fals -Dcom.sun.management.jmxremote.port=9988 -Dcom.sun.management.jmxremote
2) Java程式概況 : 可查看堆記憶體,系統線程數量,載入類數量和CPU使用率
3) 記憶體監控 : 察看記憶體詳細信息,細化到eden,survivior,老年代,永久區;可以強制Full GC
4) 線程監控 : 可以監測到所有線程以及棧信息
5) 類載入情況 : 顯示載入空間,類數量
6) 虛擬機信息 : 顯示虛擬機類型,版本,堆信息,虛擬機參數
7) MBean管理 : 可操作GC信息顯示,線程信息查看,峰值線程數量,當前線程數量,死鎖檢測等
8) 使用插件 : 如JDK自帶插件JTop,察看線程CPU使用排序
6.5 Visual VM多合一工具
1) Visual VM連接應用程式 : 添加JVM參數或者配合jstatd遠程監控
2) 監控應用程式概況 : 類似Jconsole
3) Thead Dump和分析
4) 性能分析 : 在Sampler頁面下,可以監測方法調用時間
5) 快照 : 快照導出
6) 記憶體快照分析
7) MBean管理 : 可通過插件繼承MBean
8) TDA使用 : 分析導出的線程快照,可以單獨運行,也可以作為Visual VM插件運行
9) BTrace介紹 : 在不停機的情況下添加代碼執行,
監控指定函數耗時
取得任意行代碼信息
定時觸發
監控函數參數
監控文件
6.6 Visual VM對OQL的支持
1) Visual VM的OQL基本語法
2) 內置head對象
3) 對象函數
4) 集合/統計函數
5) 程式化OQL
6.7 MAT記憶體工具分析
6.8 MAT對OQL的支持
6.9 JProfile簡介 : 商業軟體
JVM : -XX:+UseSpinning (開啟自旋鎖) -XX:PreBlockSpin (自旋鎖的等待次數)
-server -XX:+DoEscapeAnalysis (逃逸分析) -XX:+EliminateLocks (鎖消除)
-XX:+UseBiasedLocking (開啟偏向鎖)
-Xss1M
-XX:+PrintGCDetails(輸出收集器日誌)
-XX:+UseSerialGC (新生代串列收集器和老年代串列收集器)
-XX:+UseParallelGC (新生代並行回收收集器和老年代串列收集器,會停止app threads)
-XX:+UseParNewGC (新生代並行收集器和老年代串列收集器)
-XX:+UseConcMarkSweepGC (新生代並行收集器和老年代CMS)
-XX:CMSInitiatingOccupancyFraction (CMS老年代回收閥值n,預設68(%),如果記憶體使用增長快,n較小時效率高,反之n應該較大)
-XX:+UseCMSCompactAtFullCollection (CMS收集完成進行碎片整理,整理不是併發進行)
-XX:CMSFullGCsBeforeCompation (CMS進行n次後,才進行記憶體壓縮)
-XX:+UseParallelOldGC (新生代老年代並行回收處理器)
-XX:MaxGCPauseMillis (最大垃圾收集停頓時間)
-XX:GCTimeRatio (設置吞吐量大小 : n=[0~100] (gcTime <= 1/(1+n) ) )
-XX:+UseAdaptiveSizePolicy (自適應GC調節策略)
-XX:ParallelGCThreads (收集器線程數,一般等於CPU數量)
-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC (啟動G1 GC)
-XX:MaxGCPauseMillis -XX:GCPauseIntervalMillis (不超過200ms, 停頓不超過50ms,參數只是目標,不保證執行)
-XX:DisableExplicitGC (禁用顯示gc) -Xnoclassgc(禁用類元數據回收) -Xverify:none (禁用類驗證)
remote debug : 遠程debug
遠程debug顧名思義,能夠將遠程操作系統上的任何java進行debug,但是有前提是本地需要有同步的代碼。
1.遠程debug的步驟是在遠程操作系統上啟動java進程時增加特殊的
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=9977
2.在Eclipse中新建一個Remote Java Application