Oracle JDBC驅動程序可能會使用大量的內存。這是一種有意識的設計選擇,在使用大量內存與提升性能以前作出權衡。在大多數狀況下,對於大多數用戶,這已被證實是一個不錯的選擇。一些用戶已經經歷了JDBC驅動程序使用的大量內存的問題。本白皮書正是寫給這些用戶。若是您的應用程序的性能表現是可接受的,那麼你沒有理由擔憂內存使用。若是您的應用程序並無達到你所指望的性能,而且使用了比預期的更多的內存,那麼請繼續讀下去。html
7.3.4,8i和9i的oracle JDBC驅動程序,使用差很少儘量少的內存。不幸的是,這致使了不可接受的性能損失。在10g中,爲了提升性能,驅動程序的體系結構被從新設計。在該體系結構重建的最重要的變化之一是驅動程序如何使用內存。開發團隊作出了有針對性的決定,用內存換取性能。10g驅動程序所以比9i的驅動程序平均快約30%。固然,你因此測得的數據可能會有所不一樣。java
因爲內存價格相對比較便宜,伴隨着10g驅動程序發佈以後物理內存的大小也在顯着地不斷上漲,所以大多數用戶都受益於(內存換性能的)性能改進。有一些用戶,並且大部分是具備很是大規模應用的用戶,已經看到了因爲heap過大,垃圾收集器的trashing,甚至OutOfMemoryExceptions所引發的性能問題。在後續的版本中,開發團隊一直在努力解決這些問題,經過改進驅動程序使用內存的方式,爲用戶提供額外的(內存使用)控制,以解決這些具體的問題。本白皮書介紹了驅動程序是如何使用內存的,應用程序特色如何具體地影響內存的使用,以及用戶能夠作些什麼以更好地管理內存使用和提升應用程序性能。mysql
注:在本文的其他部分的單詞「驅動程序」自己是指10g及之後版本的Oracle JDBC驅動程序。其它狀況會指明特定版本或被引用的版本。sql
10g的Oracle JDBC驅動程序,具備比之前的版本更大、更復雜的類層次結構。這些類的對象存儲了更多的信息,因此須要更多的內存。這確實增長了內存的使用,但並這並非真正的問題所在。真正的問題是用來存儲查詢結果的buffer。每一個語句(包括PreparedStatement和CallableStatement的)都持有兩個緩衝區,一個byte[](字節數組)和一個char [](字符數組)。char[]用來保存全部字符類型的行數據,如:CHAR,VARCHAR2,NCHAR等,byte[]用來保存全部的其它類型的行數據。這些buffer在在SQL被解析的時候分配,通常也就是在第一次執行該Statement的時候。Statement會持有這兩個buffer,直到它被關閉。數據庫
因爲buffer是在SQL解析的時候被分配的,buffer的大小並不取決於查詢返回的行數據的實際長度,而是行數據可能的最大的長度。在SQL解析時,每列的類型是已知的,從該信息中驅動程序能夠計算存儲每一列所需的內存的最大長度。驅動程序也有fetchSize屬性,也就是每次fetch返回的行數。有了每列有大小和行數的大小,驅動程序能夠由此計算出一次fetch所返回的數據最大絕對長度。這也就是所分配的buffer的大小。數組
某些大類型,如LONG和LONG RAW,因爲太大而沒法直接存於buffer中會採起另一種不一樣的處理方式。若是查詢結果包含一個LONG或LONG RAW,將fetchSize設置爲1以後,所碰見的內存問題就會變得明朗不少了。這種類型的問題不在這裏討論了。緩存
字符數據存儲在char[] buffer中。Java中的每一個字符佔用兩個字節。一個VARCHAR2(10)列將包含最多10個字符,也就是10個Java的字符,也就是每行20個字節。一個VARCHAR2(4000)列將佔用每行8K字節。重要的實際上是column的定義大小,而不是實際數據的大小。一個VARCHAR2(4000)可是隻包含了NULL的列,仍然須要每行8K字節。buffer是在驅動程序看到的查詢結果以前被分配的,所以驅動程序必須分配足夠的內存,以應付最大可能的行大小。一個定義爲VARCHAR2(4000)的列最多可包含4000個字符。Buffer必須大到足以容納4000個字符分配,儘管實際的結果數據可能沒有那麼大。性能優化
BFILE,BLOB和CLOB會被存儲爲locator。Locator可高達4K字節,每一個BFILE,BLOB和CLOB列的byte[]必須有至少每行4K字節。RAW列最多能夠包含4K字節。其它類型的則須要不多的字節。一個合理的近似值是假設全部其它類型的列,每行佔用22個字節。服務器
CREATE TABLE TAB (ID NUMBER(10), NAME VARCHAR2(40), DOB DATE)網絡
ResultSet r = stmt.executeQuery(「SELECT * FROM TAB」);
當驅動器執行executeQuery方法,數據庫將解析SQL。數據庫會返回結果集將有三列:一個NUMBER(10),一個VARCHAR2(40),和一個DATE。第一列的須要(約)每行22個字節。第二列須要每行40個字符。第三列的須要(約)每行22個字節。所以,每行須要22 +(40 * 2)+ 22 = 124個字節。請記住,每一個字符須要兩個字節。fetchSize默認是10行,因此驅動程序將分配一個char[] 10 * 40 = 400個字符(800字節)和一個byte[] 10 *(22 + 22)= 440個字節,共1240字節。 1240字節不會致使什麼內存問題。但有一些查詢結果會比較更大一些。
在最壞的狀況下,考慮一個返回255個VARCHAR(4000)列查詢。每行每列須要8K字節。乘以255列,每行2040K字節也就是2MB。若是fetchSize設置爲1000行,那麼驅動程序將嘗試分配一個2GB的char[]。這將是很糟糕的。
做爲一個Java開發人員,讀者無疑想到其它分配和使用內存的方式。請放心,Oracle的JDBC的開發團隊也是可以想到。其它的方式都進行了測試,而後才選得這樣一種方式。雖然尚未進入具體細節,可是確實有不少好的理由讓咱們選擇這種方式。這個選擇徹底是爲了驅動程序的性能。
用戶能夠經過幾種方法來管理這些buffer的大小。
1. 當心地定義table
2. 當心地編寫查詢代碼
3. 當心地設置fetchSize的值
一個VARCHAR2(20)就夠用的列被定義成VARCHAR2(4000)會致使很是大的區別。一個VARCHAR2(4000)列的要求每行8K字節。一個VARCHAR2(20)列每行只須要40個字節。若是列事實上歷來沒有超過20個字符,而後定義一個VARCHAR2(4000)列是在浪費驅動程序分配的buffer空間。
在只須要幾列的狀況下使用SELECT *,除了從buffer大小之外,也會很是大地影響性能,。它會須要更多時間來獲取行的內容,將其轉換,而後經過網絡發送,再將其轉換爲Java表達。雖然返回了幾十列,但只有少數是須要的,會致使驅動程序分配大量的buffer來存儲那些不須要的結果。
控制內存使用的主要工具是fetchSize。雖然2MB也算比較大的,可是大多數Java環境分配這樣大的buffer不會有任何問題。即便在最壞的狀況下,若是fetchSize設置爲1,255個VARCHAR(4000)列的結果也不會在大多數應用中產生問題。
解決內存使用問題的第一步是檢查SQL。計算每一個查詢的一行數據的近似大小,而後看看fetchSize的大小。若是每行的大小是很是大,那考慮一下是否有可能獲取較少的列或修改schema使得數據更加緊密以限制行大小。最後,設置fetchSize以保持buffer在一個合理的大小。什麼是「合理的」取決於應用程序的具體狀況。Oracle建議fetchSize不要超過100,雖然在某些狀況下,更大一些的值也是合適的。就算會返回不少行,設置fetchSize爲100了多是不恰當的。
注: Oracle提供的方法OracleStatement.defineColumnType可用於減小過大的列定義的大小 。當調提供一個size參數時,size將覆蓋該schema中列定義的大小。這容許你在沒法自由修改schema的狀況下,也能夠必定程度上的解決問題。使用Thin驅動程序時,你能夠只在有問題的列上調用defineColumnType。當使用OCI驅動程序時,你能夠在具體的Statement上調用它,也能夠爲全部的Statement的全部列調用它。若是能調整schema,固然是更好的選擇。
一條statement不算問題。除了像255個VARCHAR2(4000)列或的setFetchSize(100000)等病態的狀況以外,一個單獨的statement是不太可能致使內存使用的問題。在實踐中,問題只出如今系統中有數百甚至數千Statement對象的狀況下。一個很是大的系統,可能有幾百個鏈接同時開啓。每一個鏈接可能有一個或兩個statement同時打開。這樣一個很是大的系統將在一臺具備很是大的物理內存的計算機上運行。有了合理的配置,即便這樣一個打開了數百個statement的很是大型的系統也是不太可能有嚴重的內存問題。是的,驅動程序雖然會使用大量的內存,可是內存就是要拿來拿來被用的。所以,在實踐中,即便是很是大的系統,也依然可避免內存問題。
大型系統每每不少次地執行相同的SQL。出於性能方面的考慮,重用PreparedStatement而不是每次爲每條SQL建立,將會很是有助於提升性能。如此之大的系統能夠有不少PreparedStatement,每一個特殊的SQL字符串都會有一個(或多個)。大多數大型系統使用一個模塊化的框架,如WebLogic。框架內的獨立組件建立他們所須要的PreparedStatement。這與PreparedStatements須要被重用想違背。爲了解決這個問題須要,框架提供了statement cache。有了statement cache,一個鏈接極可能會在內存中保持一百個或更多的PreparedStatement。乘以數以百計的鏈接,你就可能真得會有潛在的內存問題了。
Oracle的JDBC驅動程序經過內置的驅動程序語句緩存,Implicit Statement Cache,來解決這個問題。(也有一個叫Explicit Statement Cache的緩存,這裏不討論)。Implicit Statement Cache對用戶是透明的。用戶代碼調用prepareStatement就像建立一個新對象同樣。若是驅動程序能從cache中取到可用的PrepareStatement,就會直接返回它。若是沒有取到則建立一個新對象。用戶代碼沒法從語義上區分一個新建立的和一個重複的PrepareStatement。
從性能角度來看,從緩存中取比新建立一個statement更快。緩存的statement的執行會快得多,由於驅動程序能夠重用statement上一次執行的不少狀態。
Implicit Statement Cache知道Oracle JDBC中PreparedStatement的內部結構。所以,它能夠管理該結構,以得到最佳性能。特別是它能夠管理的char[]和byte[] buffer。驅動程序的不一樣版本提供了不一樣的buffer管理方式,這是由於Oracle更好地理解了實際的應用對於buffer管理的需求。可是無論怎麼,Oracle的JDBC驅動程序,只有當PreparedStatement返回到Implicit Statement Cache中的時候,才能夠管理的這些char[]和byte[] buffer。若是沒有關閉的PreparedStatement,那麼驅動程序有沒有辦法知道該statement將不會被當即重用,因此不能作任何事情來管理buffer的使用。
行數據buffer不是Oracle的JDBC驅動程序建立的惟一的大buffer。它們還爲發送到數據庫的PreparedStatement參數建立很大的buffer。和寫數據相比,應用程序一般讀取更多的數據,而且每次寫入的數據塊都比較小。所以參數buffer,每每要遠遠高於該行數據buffer。然而,使用(不正確的使用)statement batching,也有可能迫使驅動程序建立大的buffer。
當應用程序調用PreparedStatement.setXXX設置參數值時,驅動程序須要存儲這些值。這須要不多的內存;只是一個數組和對象類型(如String)的引用,long和double須要8個字節,其它類型須要4個字節。執行PreparedStatement時,驅動程序必須以SQL數據類型而不是Java類型來發送這些值到數據庫。驅動程序建立一個byte[]和一個char[] buffer。參數的數據轉換爲SQL類型後會存儲在這些buffer中。而後驅動程序將這些字節發送到網絡上。因爲驅動程序在分配buffer前已經知道數據的實際大小,因此它能夠建立儘可能小的buffer。並且若是一個語句屢次執行,驅動程序會試圖重用這些buffer。若是新的數據值須要一個更大的byte[]或char[]緩衝區,驅動程序會分配一個更大的buffer。只要有合適大小的內存,執行一條語句是不會產生內存不夠用的問題的。可是使用statement batching,狀況就不同了。
JDBC標準和Oracle statement batching都在一個操做中執行一個語句屢次。要作到這一點,驅動程序必須一次性發送PreparedStatement的全部execute的全部參數。這意味着驅動程序必須轉換全部參數的數據到SQL類型並存入buffer。在批處理中一個batch中executes數量,batchSize,跟查詢中的fetchSize差很少。雖然一個單獨的語句的執行所須要的參數轉換不可能會致使內存問題,但是很是大的batchSize可能就會引發問題。
在實踐中,這是一個不常見的問題,而且只有在batchSize設置得很是不合理的大的時候(數萬)纔會產生。每隔幾百行調用一次executeBatch應該就能夠解決的問題。
本節介紹不一樣版本的Oracle JDBC驅動程序是如何管理buffer的以及用戶如何調整驅動程序以得到最高的性能。
注:在討論內存管理的細節的時候,忽略其實有兩個buffer,byte[]和char[],會使得討論方便一些。在本節下面的部分,「buffer」可能同時或者分別指代兩個buffer,byte[]和char[]。
雖然Java的內存管理是至關不錯的,可是大的buffer分配仍是昂貴的。其實並非實際malloc的成本。那實際上是很是快的。相反,問題是Java語言的要求,全部這些buffer需求用零填充。因此不公要有一個很大的buffer被分配出來,它也必需要被零填充。零填充須要寫buffer的每個字節。現代的處理器處理因爲有多級data caches,處理小buffer是沒有什麼問題的。零填充一個大的buffer超出了處理器的data cache的大小,是在內存中之內存的讀寫速度進行的,這大大低於處理器的最大運行速度。性能測試已屢次代表,分配buffer,是驅動程序的一個巨大的性能上的瓶頸。這是一個比較糾結的地方,要平衡buffer分配與重用buffer所需的時間和內存成本。
最初的10g的驅動程序使用了一個比較原始的內存管理方法。這個內存管理方法是爲了得到最高的性能。當一個PreparedStatement是第一次執行,必要的byte[]和char[] buffer會被分配。就是這樣。buffer只有在PreparedStatement自己被釋放(freed)的時候纔會被釋放(freed)。Implicit Statement Cache不會爲管理buffer作任何事情。全部Implicit Statement Cache中緩存的PreparedStatements的持有着其分配的byte[]和char[] buffer準備當即被重用。所以,針對調節此版本的驅動程序內存管理的惟一途徑是經過設置setFetchSize,精心設計schema,並當心地編寫SQL查詢語句。最初的10g驅動器是至關快的,但可能有內存管理問題,包括OutOfMemoryException問題。
10.2.0.4.0驅動程序添加了一個鏈接屬性着手解決出如今初始10g驅動程序的內存管理問題。此鏈接屬性採用一種一刀切的方式。若是設置,返回一個PreparedStatement到Implicit Statement Cache中的時候會釋放其中的buffer。Statement是從cache中取出時,buffer會被從新分配。這個簡單的方法大大地減小了內存使用,可是犧牲了巨大的性能成本。如上所述,分配buffer是昂貴的。
這個鏈接屬性就是
oracle.jdbc.freeMemoryOnEnterImplicitCache
它的值是一個boolean字符串,「true」或「false」。若是設爲「true」,當一個PreparedStatement返回cache時,buffer會被釋放。若是設爲「false」(默認值也是「false」),buffer被保留,和最初的10g驅動程序同樣。該屬性能夠經過-D被設置爲System property或做爲調用getConnection方法時候的connection property。請注意,設置freeMemoryOnEnterImplicitCache不會致使參數值buffer被釋放,它只會影響該行數據buffer。
JDBC開發團隊認識到,在10.2.0.4.0使用全有或全無的方式不太理想。11.1.0.6.0驅動程序提供了一個內存管理更爲複雜的方法。此方法要實現兩個目標,最大限度地減小未內存使用量和最小化分配buffer的成本。驅動程序在每一個鏈接內部建立了一個buffer緩存(buffer cache)。當一個PreparedStatement返回到Implicit Statement Cache中的時候,它的buffer會被緩存到buffer緩存中。當一個PreparedStatement是從Implicit Statement Cache中取出時,buffer會也會同時從buffer緩存中取出。所以,Implicit Statement Cache中的PreparedStatement再也不持有大的buffer,buffer會被屢次重用而不是被屢次建立。相比10g驅動程序,無論有沒有freeMemoryOnEnterImplicitCache,都顯著地提高了驅動程序的性能。
如開篇介紹中所指出的,buffer的大小可能會有很大的不一樣,從零到幾十甚至上百MB。11.1.0.6.0的buffer緩存很是簡單,事實證實有些太簡單了。全部緩存的buffer大小都是相同的。因爲buffer能夠被任何Implicit Statement Cache中的PreparedStatement使用,全部buffer不得不足夠大以知足buffer需求最大的PreparedStatement的需求。若是同時只會有一個statement被使用,那麼只會有一個buffer,並且buffer是會被全部PreparedStatement使用的。對於一些或者是大部分statement來講,buffer可能都太大了,但buffer大小至少會正好與一個緩存中的statement須要的大小所匹配。若是PreparedStatement的重用的狀況不是太偏頗,那麼保持大的buffer而且重用它們將會得到相對比較好的性能,這是相對於只保持較小的buffer,並根據須要從新分配的較大的buffer來講的。若是同時打開多個statement,而且其中一個PreparedStatement的要求的buffer過大,這就會有一個潛在的內存問題。
考慮一個應用程序,其中一個PreparedStatement須要一個10MB的buffer,其他的須要較小的buffer。只要同時每一個鏈接只有一個PreparedStatement在被使用,而且那個大的PrepareStatement常常被重用,那是沒有什麼問題的。每一個statement在被使用的時候會分得這惟一的一個10MB的buffer,PreparedStatement返回Implicit Statement Cache的時候,buffer被返回buffer緩存。這惟一的10MB buffer只被建立一次,而且被多個PreparedStatements不斷地重複使用。如今考慮這麼一種狀況,若是兩個PreparedStatements的同時打開。兩個都須要buffer。因爲一個PreparedStatement可能被分配任何一個buffer,因此全部的buffer必須是大小相同的,也就是最大的長度大小。當兩個PreparedStatement同時打開的時候,兩個buffer必須都是10MB。打開第二個PreparedStatement的時候,即便須要的只是很小的一個buffer,依然須要建立一個10MB的buffer。若是三個statement同時打開,就要再建立第三個10MB的buffer。在一個大型系統中,當同時打開數百個鏈接和數據百個PreparedStatement的時候,給每個PreparedStatement都分配一個最大長度的的buffer可能會致使內存使用過多。這是顯而易見的,但開發團隊並無體會到這個問題有多大,並沒在內部測試的時候也沒發現這個問題。這個問題能夠經過恰當地schema設計,當心地編寫SQL,正確地設置fetchSize來適當緩解。
須要注意的是11.1.0.6.0驅動程序不支持freeMemoryOnEnterImplicitCache,當PreparedStatement返回cache的時候buffer老是會被釋放的(released)。釋放出來的buffer會被放入buffer緩存中。
11.1.0.7.0驅動程序引入了一個鏈接屬性以解決大buffer的問題。此屬性限定了保存在buffer緩存中buffer的最大的長度大小。全部超大的buffer都會在PreparedStatement返回Implicit Statement Cache的時候被釋放(freed),而且在PreparedStatement從cache中取出時再從新建立相應的buffer。若是大部分PreparedStatement都是須要適度大小的buffer,例如小於100KB,但有一些須要較大的buffer,那麼設置該屬性爲110KB會使得小的buffer被高度重用而不用承擔老是建立最大長度buffer的成本。設置此屬性能夠提升性能,甚至能夠防止OutOfMemoryException問題。
這個鏈接屬性就是
oracle.jdbc.maxCachedBufferSize
它的值是一個int字符串,例如「100000」。默認是Integer.MAX_VALUE。這是能夠存儲在buffer緩存中的buffer的最大長度大小。這個大小限制即便用於char[]又使用於byte[]的buffer。對於char[] buffer就是字符的數量,對於byte[] buffer就是字節的數量。它只是最大的buffer的大小,而不是一個預約義(predefined)的大小。若是maxCachedBufferSize設置爲100KB,但最大的buffer大小不超過100KB只有50KB,那麼buffer緩存中的buffer大小將是50KB。maxCachedBufferSize值的改變,只有將不一樣長度的char[]和byte[] buffer包括或者排除驅動程序內部的buffer緩存的時候,纔會對性能有影響。大的值的改變,即便是幾MB的變化,也可能沒帶來什麼區別。相似的,變化1個大小也可能產生巨大的區別,若是是會致使一個PreparedStatement的buffer被包含或排除在buffer緩存中。此屬性能夠經過-D來設置System property或者經過的getConnection的時候的connection property。
若是您須要設置maxCachedBufferSize,那麼開始估算須要的最大buffer的SQL查詢的buffer大小。在這個過程當中你也行會發現,經過調整這些查詢的fetchSize大小才能夠知足所需的性能需求。仔細考慮執行的頻率和buffer的大小,選擇一個合適的大小使得大多數語句可使用緩存的buffer,而且buffer的大小足夠小,足以使Java runtime能夠支持大量的buffer,以儘可能減小建立新的buffer的頻率。
有些程序有大量的(相對於線程數來說)空閒鏈接。應用可能須要鏈接到不少數據庫中的任何一個,可是每次只會鏈接其中的一個。若是差很少每個線程都使用一個鏈接去鏈接數據庫,那麼相對於線程數就會有不少的空閒鏈接。因爲默認狀況下buffer緩存是附屬於每一個鏈接的,空閒鏈接的結果就是buffer緩存中有不少不會被用到的buffer。這意味着沒有必要地佔用了更多的內存。雖然這是一種比較罕見的狀況的,但並非不爲人知的。
這種狀況的解決方法是設置鏈接屬性
oracle.jdbc.useThreadLocalBufferCache
此屬性的值是一個boolean字符串,「true」或「false」。默認值是「false」。當此屬性設置爲「true」,buffer緩存是存儲在一個ThreadLocal中,而不是直接存儲在鏈接中。若是有比鏈接較少的線程,那麼這將減小內存的使用量。該屬性能夠經過-D設置System property或者經過的調用getConnection時的connection property。
全部鏈接useThreadLocalBufferCache =「true」共享同一個靜態的ThreadLocal字段,從而使用同一組buffer緩存。useThreadLocalBufferCache =「true」的全部鏈接都必須爲maxCachedBufferSize設置相同的值。若是一個線程首先使用一個鏈接,而後又使用另外一個鏈接,兩個鏈接將因爲使用的buffer的數量和大小相互有一些間接的影響。一般狀況下,全部使用ThreadLocal的buffer緩存的鏈接都屬於同一應用,因此這不太會有什麼問題。若是一個線程建立了一個statement,另外一個線程關閉了該statement,buffer會從一個ThreadLocal的buffer緩存遷移到另外一個。這是不推薦的作法。若是全部的statement都由一個線程建立被另外一個線程關閉,那麼就不會有buffer被重用。因此,這是不推薦的,但若是你的應用就是這麼幹的,那就不設置useThreadLocalBufferCache爲「true」。讓部分鏈接使用ThreadLocal buffer緩存,部分使用默認的鏈接內部buffer緩存,這也是能夠實現的。
11.2驅動程序比11.1.0.7.0具備更復雜的buffer緩存。這個buffer緩存於多個bucket中。一個bucket的全部buffer都是相同的大小並且這個大小是預先肯定的.當第一次執行一個PreparedStatement的時候,驅動程序從一個buffer大小大於結果長度的可是又是buffer大小最小的buffer bucket中取出一個buffer(翻譯的有點繞了原文以下When a PreparedStatement is executed for the first time the driver gets a buffer from the bucket holding the smallest size buffers that will hold the result.)。若是bucket中沒有buffer,驅動程序根據bucket的預約義大小建立一個新的相應大小的buffer。當一個PreparedStatement被關閉,buffer被返回到相應的bucket中。因爲buffer被用於不定大小的需求,buffer一般只比最小的(翻譯註:應該是「最大的」吧?)需求大一些。這種差別是有限的,在實踐中沒有任何影響。
相比11.1或10.2.0.4.0驅動程序,11.2驅動程序始終會保持使用相同或更少的內存。有時候一種不正確的說法是11.2驅動程序能夠在小得性能都不可接受的heap中運行。僅僅由於它能夠運行在更少的內存中,並不意味着它應該就這樣部署。爲11.2驅動程序設置一個較大的-Xms值來大副地提升性能,這並非一種罕見行爲。請參閱下面的控制java Heap。
11.2驅動程序支持maxCachedBufferSize,但它明顯再也不那麼重要。在11.1中,正確地設置maxCachedBufferSize可能就會從OutOfMemoryException的境地變爲性能優越。在11.2的驅動程序中,maxCachedBufferSize的設置有時能夠改善那些很是大型、擁有大量statement緩存、和大量對於buffer大小需求明顯不一樣的SQL的大型系統的性能。在11.2的maxCachedBufferSize值被解釋爲buffer的最大大小是該值以2爲底的對數。例如,若是maxCachedBufferSize設置爲20的緩衝區緩存的最大大小爲2^20 = 1048576。爲了向後兼容,大於30的值被解釋爲實際大小,而不是log2的大小,但建議2的對數方式設定此值。
在一般狀況下,將maxCachedBufferSize設置一個合理的值是沒有任何影響的。若是您須要設置maxCachedBufferSize,從18開始嘗試。若是你設置的值不得不小於16,那你可能須要更多的內存。
在11.2驅動程序中,參數數據buffer使用的是相同的buffer緩存和緩存方案。當一個PreparedStatement被放入Implicit Statement Cache中時,參數數據buffer會被buffer緩存緩存起來。當一個PreparedStatement第一次被執行或第一次從Implicit Statement Cache中取出後執行,它就會從buffer緩存獲取參數數據buffer。一般狀況下,參數數據buffer的大小遠遠小於於返回的行數據buffer的大小,可是這些buffer也可能因爲batch size過大而變得很大。11.2驅動程序也爲其它大型字段操做如Bfile、Blob和Clob提供了大的byte[]和char[] buffer的buffer緩存。
11.2驅動程序也支持使用useThreadLocalBufferCache。它的功能和關於什麼時候以及如何使用它的建議與11.1.0.7.0中相同。
11.2驅動程序還添加了一個新的屬性,以控制Implicit Statement Cache。
oracle.jdbc.implicitStatementCacheSize
這個屬性的值是一個整型字符串,如「100」它是statement cache的初始大小。將該屬性設置爲正值,便是開啓Implicit Statement Cache。默認值爲‘0’。該屬性能夠經過-D設置爲Sytstem property或經過調用getConnection時的connection property設置。調用OracleConnection.setStatementCacheSize和/或OracleConnection.setImplicitCachingEnabled將覆蓋implicitStatementCacheSize的值。此屬性可使沒法在代碼中啓用Implicit Statement Cache的狀況下更方便地啓動此功能。
優化Java運行時內存使用是一個有一點黑盒的藝術。最重要的兩個選項是-Xmx和-Xms。根據Java運行時版本和操做系統的不一樣,還有一些其它的參數。-Xmx設置Java運行時可使用的最大的heap的大小。 -Xms剛設置初始heap的大小。默認值取決於操做系統和Java運行時版本,但通常的-Xmx的默認值是64MB,-Xms的默認值是1MB。 32位的JVM支持高達2GB的heap。 64位JVM支持更大的heap。這些參數接受「k」,「m」,和「g」做爲後綴以代表是kilo-、mega-仍是giga-個字節,例如,-Xmx1g。
當有足夠的heap大小時,Oracle的JDBC驅動程序能夠提供最佳的性能。對於大多數應用,增長heap的大小達到一個下限以後,應用的性能會大幅提高。大小超出該下限以後,將沒有什麼區別。若是heap的大小是如此之大以致於超出機器上運行的物理內存,heap內存將會被換出到二級存儲,性能會受到嚴重的影響。設置heap大小時,只設置-Xmx是不夠的。11.2驅動程序,特別地注重使用盡可能少的內存,它通常不會使heap上漲超出其能夠運行的最低水平。若是你只設置最大heap大小,-Xmx,驅動程序可能永遠不會實際使用到那麼大的內存。若是你還經過-Xms增長最小heap的大小,驅動程序將會可使用更多的內存,並住住能提供更好的性能。作一個固定的heap大小是的性能測試是很重要的,即將-Xmx和-Xms設置成相同的值。通常生產環境中的應用也是運行在固定的heap大小中的。設置-server選項會使JVM減少最小化heap大小的努力,因此會提供必定的性能改進。適當地設置-Xms每每會比單獨設置-server選項提供更多的性能改進。Oracle建議爲服務器應用設置-server,-Xms,和-Xmx選項。一般-Xms和-Xmx應設置爲相同的值。
Oracle的JDBC驅動程序可能會使用大量的內存以達到最大的性能優化。若是內存制約了應用程序的性能,你能夠調整它以得到最大的性能優化。理想的狀況下,你應該有一個能夠用來場景重現的測試案例,以模擬實現的應用的工做負載。經過系統地改變各類參數並運行測試,你能夠找出最優化的設置組合。對於全部版本的Oracle JDBC驅動程序,啓用implicit statement cache,當心地設計數據庫schema,仔細地編寫SQL查詢,並適當設置fetchSize都首先要作的。下一步是增長Java堆大小設置-Xmx和-Xms以達到最大的實際可用大小。若是這樣達到可接受的性能,你能夠嘗試減小-Xmx和-Xms,若是一個小一些的heap能提供更多的好處的話。若是最大的實際可用內存的性能仍是比預期的要差,你應該嘗試適當地設置freeMemoryOnEnterImplictCache或maxCachedBufferSize。
應該明確你的應用的特色是否適合啓用useThreadLocalBufferCache。若是您須要將性能優化到極致,嘗試調整Java的垃圾收集器。 (見http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html)在大多數狀況下,設置-Xmx和-Xms就足夠了。
Oracle的JDBC內存管理2009年8月道格拉斯Surber
甲骨文公司世界總部Redwood Shores的甲骨文大路500,CA 94065 USA
版權全部© 2009年,甲骨文公司及/或其附屬公司。版權全部。本文檔僅用於提供信息和內容若有變動,恕不另行通知。本文檔不保證沒有錯誤,也不受任何其餘擔保或條件,不管是口頭表達或隱含在法律上,包括 適銷性或針對特定用途的適用性 的暗示擔保和 合做nditions 。咱們明確拒絕與本文檔有關的任何責任,本文檔不直接或間接造成任何合同義務。這個文件,不得轉載或以任何形式或以任何方式,爲目的,未經咱們事先書面許可,任何電子或機械,傳送。
Oracle是甲骨文公司及/或其附屬公司的註冊商標。其餘名稱多是各自 全部者 的商標 。
全球諮詢熱線:電話:+1.650.506.7000傳真:+1.650.506.7200oracle.com 0109