原來地址
http://blog.csdn.net/jaminwm/archive/2007/03/26/1541767.aspx
調優背景
HBCZT信息中心使用
IBM X366
服務器
Windows2003
運行其基于
J2EE1.4
技術的應用系統。另外運行一個基于
COM
技術的數據采集應用程序。該程序客戶端讀入用戶填寫的
xls
格式表格文件信息,并通過該程序將
XLS
內容封裝成為
XML
并打包
ZIP
后發送到數據采集程序的服務器端,服務器端接受到文件后,對該
ZIP
包進行解包、并對解包后的
XML
信息進行解析、使用
SQL
逐條將記錄插入到
Oracle
數據庫中。數據庫連接池已經設置為
20
,但批量數據插入數據庫的時候(數據量至少
500000
條記錄,一般情況
5000000
條記錄)導致數據庫異常緩慢。客戶希望找到系統瓶頸,并提出相應性能調優建議。
l
???????
總體思路
硬件調優、操作系統調優,數據庫調優
略!我們假設都已經是最佳狀態。由于本人負責
WebLogic
部分的調優,所以以下思路與內容均為
WebLogic
方面。特此說明
J2EE
應用架構環境下的系統調優,首先我們一般會從應用程序出發,去審核代碼,做到代碼級的優化,然后再調整應用服務器
(BEA WebLogic8.1)
和數據庫
(Oracle9i)
的參數,最后當然是調整操作系統和網絡的性能
(
包括硬件升級
)
。這是一種
MDA
的先進做法。誠然,在這樣一個政務項目中,不可能完全按照這個思路來做,我們把目標首先定位在應用系統所在的應用服務器
(BEA WebLogic8.1)
上,通過對
BEA WebLogic8.1
的參數進行設置,使
WebLogic8.1
能夠在最優化的環境中去運行其系統,然后對
ORACLE
數據的參數進行優化設置,最后進行性能測試再找出導致性能瓶頸所在的
SQL
代碼或
JAVA
程序,考量其修改的可行性,并進行最終問題優先級認定,與瓶頸模塊進行協商解決性能問題。當然,一般情況下我見過的案例都是出現了性能問題后才想到調優,而且一般都是先進行系統參數調整,實在解決不了才會對代碼進行檢查
.
實際上,我們應當將代碼級的調優放在應用設計時來做,測試生產時修改代碼將是一件極其痛苦的事情。
下表為一般性
J2EE
性能調優的參照情況一覽表,供參考。
毛病
|
描述
|
癥狀
|
原因或治法
|
線性內存泄漏
|
每單位
(
每事務、每用戶等
)
泄漏造成內存隨著時間或負載線性增長。這會隨著時間或負載增長降低系統性能。只有重啟才有可能恢復。
|
隨著時間越來越慢
隨著負載越來越慢
|
雖然可能有多種外部原因,但最典型的是與資源泄漏有關
(
例如,每單位數據的鏈表存儲,或者沒有回收的回收
/
增長緩沖區
)
。
|
指數方式內存泄漏
|
雙倍增長策略的泄漏造成系統內存消耗表現為時間的指數曲線
|
隨著時間越來越慢
隨著負載越來越慢
|
通常是由于向集合
(Vector
,
HashMap)
中加入永遠不刪除的元素造成的。
|
糟糕的編碼:無限循環
|
線程在
while(true)
語句以及類似的語句里阻塞。
|
可以預見的鎖定
|
您需要對循環進行大刀闊斧的刪剪。
|
資源泄漏
|
JDBC
語句,
CICS
事務網關連接,以及類似的東西被泄漏了,造成對
Java
橋接層和后端系統的影響。
|
隨著時間越來越慢
可以預見的鎖定
突然混亂
|
通常情況下,這是由于遺漏了
finally
塊,或者更簡單點,就是忘記用
close()
關閉代表外部資源的對象所造成的。
|
外部瓶頸問題
|
后端或者其他外部系統(如鑒權)越來越慢,同樣減緩了
J2EE
應用服務器和應用程序
|
持續緩慢
隨著負載越來越慢
|
咨詢專家(負責的第三方或者系統管理員),獲取解決外部瓶頸問題的方法。
|
外部系統
|
J2EE
應用程序通過太大或太多的請求濫用后端系統。
|
持續緩慢
隨著負載越來越慢
|
清除冗余的工作請求
,成批處理相似的工作請求,把大的請求分解成若干個更小的請求,調整工作請求或后端系統
(
例如,公共查詢關鍵字的索引
)
等。
|
糟糕的編碼:
CPU
密集的組件
|
這是
J2EE
世界中常見的感冒。一些糟糕的代碼或大量代碼之間一次糟糕的交互,就掛起了
CPU
,把吞吐速度減慢到爬行的速度。
|
持續緩慢
隨著負載越來越慢
|
典型的解決方案就是數據高速緩存或者性能計數。
|
中間層問題
|
實現得很糟糕的橋接層
(JDBC
驅動程序,到傳統系統的
CORBA
連接
)
,由于對數據和請求不斷的排列、解除排列,從而把所有通過它的流量減慢到爬行速度。這個毛病在早期階段很容易與外部瓶頸混淆。
|
持續緩慢
隨著負載越來越慢
|
檢查橋接層和外部系統的版本兼容性。如果有可能,評估不同的橋接供應商。如果重新規劃架構,有可能完全不需要橋接。
|
內部資源瓶頸:過度使用或分配不足
|
內部資源
(
線程、放入池的對象
)
變得稀缺。是在正確使用的情況下加大負載時出現過度使用還是因為泄漏?
|
隨著負載越來越慢
零星的掛起或異常錯誤
|
分配不足:根據預期的最大負載提高池的最大尺寸。過度使用:請參閱外部系統的過度使用。
|
不停止的重試
|
這包括對失敗請求連續的
(
或者在極端情況下無休止的
)
重試。
|
可以預見的鎖定
突然混亂
|
可能就是后端系統完全宕機。在這里,可用性監控會有幫助,或者就是把嘗試與成功分開。
|
線程:阻塞點
|
線程在過于積極的同步點上備份,造成交通阻塞。
|
隨著負載越來越慢
零星的掛起或異常錯誤
可以預見的鎖定
突然混亂
|
可能同步是不必要的
(
只要重新設計
)
,或者比較外在的鎖定策略
(
例如,讀
/
寫鎖
)
也許會有幫助。
|
線程:死鎖
/
活動鎖
|
最普遍,這是您基本的“獲得順序”的問題。
|
突然混亂
|
處理選項包括:主鎖,確定的獲得順序,以及銀行家算法。
|
?
l
???????
調優建議
通過分析其配置。我們發現
JDBC
連接池存在性能問題。
在
WebLogic
中就大量使用了池
:JDBC Connection Pool
、
Socket Pool
、
Object Pool
和
Thread Pool
。
I/O
操作中,
buffer
是必須的,特別是對大文件的操作,不然容易造成內存溢出。字節操作最快,所以盡可能采用
write(byte[])
,
Buffered FileOutputStream
比
Buffered FileWriter
要快,因為
FileWriter
需要
Unicode
到
Byte
的轉換。
JDBC
建議使用
buffer
和
cache
。為
HttpServletResponse
設置
buffersize
,使用
wl-cache
,緩存在
JNDI
樹上獲取的對象等等。
此外,使用
JDK 1.4
的非阻塞
I/O
對性能也有很大提高。
JDBC
代碼調優最大的原則就是使用
WebLogic
的連接池,而不是自己直連數據庫。在我接觸的很多自己實現連接池的項目中,大部分遇到死鎖和連接泄漏的問題,最后得不得修改代碼。而
WebLogic
提供了功能強大,性能良好的數據庫連接池,我們要做的只是封裝一個連接管理類,從
JNDI
樹上獲取數據源并緩存,得到連接,并提供一系列關閉數據庫資源的方法。
對任何資源使用的原則是用完即關,不管是數據庫資源、上下文環境,還是文件。數據庫資源的泄漏極易造成內存泄漏,乃至系統崩潰。在使用完數據庫資源后依次關閉
ResultSet
,
Statement
和
Connection
,而在一個數據庫連接多次進行數據庫操作時要特別注意
ResultSet
和
Statement
依次關閉。
由于獲取連接時默認自動提交方式,使用
connection.setAutoCommit(false)
關閉自動提交,使用
PreparedStatement
,批量更新,業務復雜或者大數據量操作時使用存儲過程,盡量使用
RowSet
,此外設置記錄集讀取緩存
FetchSize
和設置記錄集讀取方向
FetchDirection
對性能也有一定的提高。
Servlet
代碼調優比較簡單:在
Servlet
之間跳轉時,
forward
比
sendRedirect
更有效;設置
HttpServletResponse
緩沖區,如:
response.setBufferSize(20000);
在
init()
方法里緩存靜態數據,而在
destroy()
中釋放它;建議在
Servlet
里使用
ServletOutputStream
輸出圖片等對象;避免在
Servlet
和
Jsp
中定界事務等。
JDBC Connection Pool
的調優受制于
WebLogic Server
線程數的設置和數據庫進程數,游標的大小。通常我們在一個線程中使用一個連接,所以連接數并不是越多越好,為避免兩邊的資源消耗,建議設置連接池的最大值等于或者略小于線程數。同時為了減少新建連接的開銷,將最小值和最大值設為一致。增加
Statement Cache Size
對于大量使用
PreparedStatement
對象的應用程序很有幫助,
WebLogic
能夠為每一個連接緩存這些對象,此值默認為
10
。在保證數據庫游標大小足夠的前提下,可以根據需要提高
Statement Cache Size
。比如當你設置連接數為
25
,
Cache Size
為
10
時,數據庫可能需要打開
25*10=250
個游標。不幸的是,當遇到與
PreparedStatement Cache
有關的應用程序錯誤時,你需要將
Cache Size
設置為
0
。盡管
JDBC Connection Pool
提供了很多高級參數,在開發模式下比較有用,但大部分在生產環境下不需調整。這里建議最好不要設置測試表,
同時
Test Reserved Connections
和
Test Released Connections
也無需勾上。
當然如果你的數據庫不穩定,時斷時續,你就可能需要上述的參數打開。
最后分析一下
JDBC
驅動程序類型的選擇,
Oracle
提供
thin
驅動和
oci
驅動,從性能上來講,
oci
驅動強于
thin
驅動,特別是大數據量的操作。但在簡單的數據庫操作中,性能相差不大。所以我建議對數據量至少
500000
條記錄,一般情況
5000000
條記錄的狀況使用
oci
驅動。
通過分析其日志并使用
GC
資源查看。我們發現存在內存泄露的垃圾回收失敗問題。
垃圾收集
(GC)
是指
JVM
釋放
Java
堆中不再使用的對象所占用的內存的過程,而
Java
堆
(Heap)
是指
Java
應用程序對象生存的空間。堆大小決定了
GC
的頻度和時間。堆越大,
GC
頻度低,速度慢。堆越小,
GC
頻度高,速度快。所以
GC
和堆大小是一組矛盾。為了獲取理想的
Heap
堆大小,需要使用
-verbosegc
參數
(Sun jdk: -Xloggc:)
以打開詳細的
GC
輸出。分析
GC
的頻度和時間,結合應用最大負載所需內存情況,得出堆的大小。通常情況下,我們建議使用可用內存
(
除操作系統和其他應用程序占用之外的內存
)70-80%
,為避免堆大小調整引起的開銷,設置內存堆的最小值等于最大值即
:-Xms=-Xmx
。而為了防止內存溢出,建議在生產環境堆大小至少為
256M(Platform
至少
512M)
,實際環境中
512M~1G
左右性能最佳,
2G
以上是不可取的,在調整內存時可能需要調整核心參數進程的允許最大內存數。對于
sun
和
hp
的
jvm
,永久域太小
(
默認
4M)
也可能造成內存溢出,應增加參
-XX:MaxPermSize=128m
。建議設置臨時域
-Xmn
的大小為
-Xmx
的
1/4~1/3
,
SurvivorRatio
為
8
。
為了獲得更好的性能,建議在啟動文件設置
WebLogic
為產品模式,此時
sun
和
hp jvm JIT
引擎為
-server
,默認情況下打開
JIT
編譯模式對性能也有幫助。調整
Chunk Size
和
Chunk Pool Size
也可能對系統的吞吐量有提高。此外還需關閉顯示
GC: -XX:+DisableExplicitGC
。
建議
Intel
平臺上使用
jRockit
(使用參數
-jrockit
)無疑大大提高
WebLogic
性能。本系統使用
SUN JDK1.4.2_08
。
jRockit
支持四種垃圾收集器:分代復制收集器、單空間并發收集器、分代并發收集器和并行收集器。默認狀態下,
JRockit
使用分代并發收集器。要改變收集器,可使用
-Xgc:
,對應四個收集器分其他為
gencopy
,
singlecom
,
gencon
以及
parallel
。為得到更好的響應性能,應該使用并發垃圾回收器:
-Xgc:gencon
,可使用
-Xms
和
-Xmx
設置堆棧的初始大小和最大值,要設置護理域
-Xns
為
-Xmx
的
10%
。而如果要得到更好的性能,應該選用并行垃圾回收器
:-Xgc: parallel
,由于并行垃圾回收器不使用
nursery
,不必設置
-Xns
。
jRockit
還提供了強大的圖形化監控工具
Jrockit Management Console
??梢圆榭?/font>
GC
性能問題。
通過實時查看并分析操作系統與
ORACLE
系統中的
I/O
信息。我們發現存在
I/O
讀寫占用資源率高且頻繁問題。
WebLogic Server有兩套套接字復用器:Java版和本地庫。采用小型本地庫更有效,盡量激活Enable Native IO(默認),此時UNIX默認使用CPUs+1個線程,Window下為雙倍CPU。如果系統不能加載本地庫,將會拋出一個異常:java.lang.UnsatisfiedLinkException,此時只能使用Java套接字復用器,可以調整socket readers 百分比,默認為33%。該參數可以在Console Server Tuning Configuration配置欄里設置。