提高JAVA的性能,一般考慮如下的四個主要方面:
創(chuàng)新互聯(lián)公司成立與2013年,先為漳州等服務建站,漳州等地企業(yè),進行企業(yè)商務咨詢服務。為漳州企業(yè)網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。
(1) 程序設計的方法和模式
一個良好的設計能提高程序的性能,這一點不僅適用于JAVA,也適用也任何的編程語言。因為它充分利用了各種資源,如內存,CPU,高速緩存,對象緩沖池及多線程,從而設計出高性能和可伸縮性強的系統(tǒng)。
當然,為了提高程序的性能而改變原來的設計是比較困難的,但是,程序性能的重要性常常要高于設計上帶來的變化。因此,在編程開始之前就應該有一個好的設計模型和方法。
(2) JAVA布署的環(huán)境。
JAVA布署的環(huán)境就是指用來解釋和執(zhí)行JAVA字節(jié)碼的技術,一般有如下五種。即解釋指令技術(Interpreter Technology),及時編譯的技術(Just In Time Compilier Technology), 適應性優(yōu)化技術(Adaptive Optimization Technology), 動態(tài)優(yōu)化,提前編譯為機器碼的技術(Dynamic Optimization,Ahead Of Time Technology)和編譯為機器碼的技術(Translator Technology).
這些技術一般都通過優(yōu)化線程模型,調整堆和棧的大小來優(yōu)化JAVA的性能。在考慮提高JAVA的性能時,首先要找到影響JAVA性能的瓶頸(BottleNecks),在確認了設計的合理性后,應該調整JAVA布署的環(huán)境,通過改變一些參數來提高JAVA應用程序的性能。具體內容見第二節(jié)。
(3) JAVA應用程序的實現(xiàn)
當討論應用程序的性能問題時,大多數的程序員都會考慮程序的代碼,這當然是對的,當更重要的是要找到影響程序性能的瓶頸代碼。為了找到這些瓶頸代碼,我們一般會使用一些輔助的工具,如Jprobe,Optimizit,Vtune以及一些分析的工具如TowerJ Performance等。這些輔助的工具能跟蹤應用程序中執(zhí)行每個函數或方法所消耗掉的時間,從而改善程序的性能。
(4) 硬件和操作系統(tǒng)
為了提高JAVA應用程序的性能,而采用跟快的CPU和更多的內存,并認為這是提高程序性能的唯一方法,但事實并非如此。實踐經驗和事實證明,只有遭到了應用程序性能的瓶頸,從而采取適當得方法,如設計模式,布署的環(huán)境,操作系統(tǒng)的調整,才是最有效的。
3.程序中通常的性能瓶頸。
所有的應用程序都存在性能瓶頸,為了提高應用程序的性能,就要盡可能的減少程序的瓶頸。以下是在JAVA程序中經常存在的性能瓶頸。
了解了這些瓶頸后,就可以有針對性的減少這些瓶頸,從而提高JAVA應用程序的性能
4. 提高JAVA程序性能的步驟
為了提高JAVA程序的性能,需要遵循如下的六個步驟。
a) 明確對性能的具體要求
在實施一個項目之前,必須要明確該項目對于程序性能的具體要求,如:這個應用程序要支持5000個并發(fā)的用戶,并且響應時間要在5秒鐘之內。但同時也要明白對于性能的要求不應該同對程序的其他要求沖突。
1、組織與風格
(1).關鍵詞和操作符之間加適當的空格。
(2).相對獨立的程序塊與塊之間加空行
(3).較長的語句、表達式等要分成多行書寫。
(4).劃分出的新行要進行適應的縮進,使排版整齊,語句可讀。
(5).長表達式要在低優(yōu)先級操作符處劃分新行,操作符放在新行之首。
(6).循環(huán)、判斷等語句中若有較長的表達式或語句,則要進行適應的劃分。
(7).若函數或過程中的參數較長,則要進行適當的劃分。
(8).不允許把多個短語句寫在一行中,即一行只寫一條語句。
(9).函數或過程的開始、結構的定義及循環(huán)、判斷等語句中的代碼都要采用縮進風格。
注:如果大家有興趣可以到安安DIY創(chuàng)作室博客,有相關說明性的文章和解釋。
2、注解
Java 的語法與 C++ 及為相似,那么,你知道 Java 的注釋有幾種嗎?是兩種?
// 注釋一行
/* ...... */ 注釋若干行
不完全對,除了以上兩種之外,還有第三種,文檔注釋:
/** ...... */ 注釋若干行,并寫入 javadoc 文檔
注釋要簡單明了。
String userName = null; //用戶名
邊寫代碼邊注釋,修改代碼同時修改相應的注釋,以保證注釋與代碼的一致性。
在必要的地方注釋,注釋量要適中。注釋的內容要清楚、明了,含義準確,防止注釋二義性。
保持注釋與其描述的代碼相鄰,即注釋的就近原則。
對代碼的注釋應放在其上方相鄰位置,不可放在下面。對數據結構的注釋應放在其上方相鄰位置,不可放在下面;對結構中的每個域的注釋應放在此域的右方;
同一結構中不同域的注釋要對齊。
變量、常量的注釋應放在其上方相鄰位置或右方。
全局變量要有較詳細的注釋,包括對其功能、取值范圍、哪些函數或過程存取它以及存取時注意事項等的說明。
在每個源文件的頭部要有必要的注釋信息,包括:文件名;版本號;作者;生成日期;模塊功能描述(如功能、主要算法、內部各部分之間的關系、該文件與其它文件關系等);主要函數或過程清單及本文件歷史修改記錄等。
/**
* Copy Right Information : Neusoft IIT
* Project : eTrain
* JDK version used : jdk1.3.1
* Comments : config path
* Version : 1.01
* Modification history :2003.5.1
* Sr Date Modified By Why What is modified
* 1. 2003.5.2 Kevin Gao new
**/
在每個函數或過程的前面要有必要的注釋信息,包括:函數或過程名稱;功能描述;輸入、輸出及返回值說明;調用關系及被調用關系說明等
/**
* Description :checkout 提款
* @param Hashtable cart info
* @param OrderBean order info
* @return String
*/
public String checkout(Hashtable htCart,
OrderBean orderBean)
throws Exception{
}
javadoc注釋標簽語法
@author 對類的說明 標明開發(fā)該類模塊的作者
@version 對類的說明 標明該類模塊的版本
@see 對類、屬性、方法的說明 參考轉向,也就是相關主題
@param 對方法的說明 對方法中某參數的說明
@return 對方法的說明 對方法返回值的說明
@exception 對方法的說明 對方法可能拋出的異常進行說明
3、命名規(guī)范
定義這個規(guī)范的目的是讓項目中所有的文檔都看起來像一個人寫的,增加可讀性,減少項目組中因為換人而帶來的損失。(這些規(guī)范并不是一定要絕對遵守,但是一定要讓程序有良好的可讀性)較短的單詞可通過去掉元音形成縮寫;要不然最后自己寫的代碼自己都看不懂了,那可不行。
較長的單詞可取單詞的頭幾發(fā)符的優(yōu)先級,并用括號明確表達式的操作順序,避免使用默認優(yōu)先級。
使用匈牙利表示法
Package 的命名
Package 的名字應該都是由一個小寫單詞組成。
package com.neu.util
Class 的命名
Class 的名字必須由大寫字母開頭而其他字母都小寫的單詞組成,對于所有標識符,其中包含的所有單詞都應緊靠在一起,而且大寫中間單詞的首字母。
public class ThisAClassName{}
Class 變量的命名
變量的名字必須用一個小寫字母開頭。后面的單詞用大寫字母開頭
userName , thisAClassMethod
Static Final 變量的命名
static Final 變量的名字應該都大寫,并且指出完整含義。
/**
*DBConfig PATH
**/
public static final String
DB_CONFIG_FILE_PATH =com.neu.etrain.dbconfig;
參數的命名
參數的名字必須和變量的命名規(guī)范一致。
數組的命名
數組應該總是用下面的方式來命名:
byte[] buffer;
而不是:
byte buffer[];
方法的參數
使用有意義的參數命名,如果可能的話,使用和要賦值的字段一樣的名字:
SetCounter(int size){
this.size = size;
}
4、文件樣式
所有的 Java(*.java) 文件都必須遵守如下的樣式規(guī)則:
版權信息
版權信息必須在 java 文件的開頭,比如:
/*
* Copyright ? 2000 Shanghai XXX Co. Ltd.
* All right reserved.
*/
其他不需要出現(xiàn)在 javadoc 的信息也可以包含在這里。
Package/Imports
package 行要在 import 行之前,import 中標準的包名要在本地的包名之前,而且按照字母
順序排列。如果 import 行中包含了同一個包中的不同子目錄,則應該用 * 來處理。
package hotlava.net.stats;
import java io.*;
import java.util.Observable;
import hotlava.util.Application;
這里 java。io.* 使用來代替InputStream and OutputStream 的。
Class
接下來的是類的注釋,一般是用來解釋類的。
/**
* A class representing a set of packet and byte counters
* It is observable to allow it to be watched, but only
* reports changes when the current set is complete
*/
接下來是類定義,包含了在不同的行的 extends 和 implements
public class CounterSet
extends Observable
implements Cloneable
Class Fields
接下來是類的成員變量:
/**
* Packet counters
*/
protected int[] packets;
public 的成員變量必須生成文檔(JavaDoc)。proceted、private和 package 定義的成
員變量如果名字含義明確的話,可以沒有注釋。
存取方法
接下來是類變量的存取的方法。它只是簡單的用來將類的變量賦值獲取值的話,可以簡單的
寫在一行上。
/**
* Get the counters
* @return an array containing the statistical data. This array has been
* freshly allocated and can be modified by the caller.
*/
public int[] getPackets() { return copyArray(packets, offset); }
public int[] getBytes() { return copyArray(bytes, offset); }
public int[] getPackets() { return packets; }
public void setPackets(int[] packets) { this.packets = packets; }
其它的方法不要寫在一行上
構造函數
接下來是構造函數,它應該用遞增的方式寫(比如:參數多的寫在后面)。
訪問類型 (public, private 等.) 和 任何 static, final 或 synchronized 應該在一行
中,并且方法和參數另寫一行,這樣可以使方法和參數更易讀。
public
CounterSet(int size){
this.size = size;
}
克隆方法
如果這個類是可以被克隆的,那么下一步就是 clone 方法:
public
Object clone() {
try {
CounterSet obj = (CounterSet)super.clone();
obj.packets = (int[])packets.clone();
obj.size = size;
return obj;
}catch(CloneNotSupportedException e) {
throw new InternalError(Unexpected CloneNotSUpportedException: +
e.getMessage());
}
}
類方法
下面開始寫類的方法:
/**
* Set the packet counters
* (such as when restoring from a database)
*/
protected final
void setArray(int[] r1, int[] r2, int[] r3, int[] r4)
throws IllegalArgumentException
{
//
// Ensure the arrays are of equal size
//
if (r1.length != r2.length || r1.length != r3.length || r1.length != r4.length)
throw new IllegalArgumentException(Arrays must be of the same size);
System.arraycopy(r1, 0, r3, 0, r1.length);
System.arraycopy(r2, 0, r4, 0, r1.length);
}
toString 方法
無論如何,每一個類都應該定義 toString 方法:
public
String toString() {
String retval = CounterSet: ;
for (int i = 0; i data.length(); i++) {
retval += data.bytes.toString();
retval += data.packets.toString();
}
return retval;
}
}
main 方法
如果main(String[]) 方法已經定義了, 那么它應該寫在類的底部.
5、代碼可讀性
避免使用不易理解的數字,用有意義的標識來替代。
不要使用難懂的技巧性很高的語句。
源程序中關系較為緊密的代碼應盡可能相鄰。
6、代碼性能
在寫代碼的時候,從頭至尾都應該考慮性能問題。這不是說時間都應該浪費在優(yōu)化代碼上,而是我們時刻應該提醒自己要注意代碼的效率。比如:如果沒有時間來實現(xiàn)一個高效的算法,那么我們應該在文檔中記錄下來,以便在以后有空的時候再來實現(xiàn)她。
不是所有的人都同意在寫代碼的時候應該優(yōu)化性能這個觀點的,他們認為性能優(yōu)化的問題應該在項目的后期再去考慮,也就是在程序的輪廓已經實現(xiàn)了以后。
不必要的對象構造
不要在循環(huán)中構造和釋放對象
使用 StringBuffer 對象
在處理 String 的時候要盡量使用 StringBuffer 類,StringBuffer 類是構成 String 類的基礎。
String 類將 StringBuffer 類封裝了起來,(以花費更多時間為代價)為開發(fā)人員提供了一個安全的接口。當我們在構造字符串的時候,我們應該用 StringBuffer 來實現(xiàn)大部分的工作,當工作完成后將 StringBuffer 對象再轉換為需要的 String 對象。比如:如果有一個字符串必須不斷地在其后添加許多字符來完成構造,那么我們應該使用StringBuffer 對象和她的 append() 方法。如果我們用 String 對象代替StringBuffer 對象的話,會花費許多不必要的創(chuàng)建和釋放對象的 CPU 時間。大家可以來安安DIY創(chuàng)作室一起討論。
避免太多的使用 synchronized 關鍵字避免不必要的使用關鍵字 synchronized,應該在必要的時候再使用她,這是一個避免死鎖的好方法。
7、編程技巧
byte 數組轉換到 characters
為了將 byte 數組轉換到 characters,你可以這么做:
Hello world!.getBytes();
Utility 類
Utility 類(僅僅提供方法的類)應該被申明為抽象的來防止被繼承或被初始化。
初始化
下面的代碼是一種很好的初始化數組的方法:
objectArguments = new Object[] { arguments };
枚舉類型
JAVA 對枚舉的支持不好,但是下面的代碼是一種很有用的模板:
class Colour {
public static final Colour BLACK = new Colour(0, 0, 0);
public static final Colour RED = new Colour(0xFF, 0, 0);
public static final Colour GREEN = new Colour(0, 0xFF, 0);
public static final Colour BLUE = new Colour(0, 0, 0xFF);
public static final Colour WHITE = new Colour(0xFF, 0xFF, 0xFF);
}
這種技術實現(xiàn)了RED, GREEN, BLUE 等可以象其他語言的枚舉類型一樣使用的常量。
他們可以用 '==' 操作符來比較。
但是這樣使用有一個缺陷:如果一個用戶用這樣的方法來創(chuàng)建顏色 BLACK new Colour(0,0,0)
那么這就是另外一個對象,'=='操作符就會產生錯誤。她的 equal() 方法仍然有效。由于這個原因,這個技術的缺陷最好注明在文檔中,或者只在自己的包中使用。
8、編寫格式
代碼樣式
代碼應該用 unix 的格式,而不是 windows 的(比如:回車變成回車+換行)
文檔化
必須用 javadoc 來為類生成文檔。不僅因為它是標準,這也是被各種 java 編譯器都認可的方法。使用 @author 標記是不被推薦的,因為代碼不應該是被個人擁有的。
縮進
縮進應該是每行2個空格. 不要在源文件中保存Tab字符. 在使用不同的源代碼管理工具時Tab字符將因為用戶設置的不同而擴展為不同的寬度.如果你使用 UltrEdit 作為你的 Java 源代碼編輯器的話,你可以通過如下操作來禁止保存Tab字符, 方法是通過 UltrEdit中先設定 Tab 使用的長度室2個空格,然后用 Format|Tabs to Spaces 菜單將 Tab 轉換為空格。
頁寬
頁寬應該設置為80字符. 源代碼一般不會超過這個寬度, 并導致無法完整顯示, 但這一設置也可以靈活調整. 在任何情況下, 超長的語句應該在一個逗號或者一個操作符后折行. 一條語句折行后, 應該比原來的語句再縮進2個字符.
{} 對
{} 中的語句應該單獨作為一行. 例如, 下面的第1行是錯誤的, 第2行是正確的:
if (i0) { i ++ }; // 錯誤, { 和 } 在同一行
if (i0) {
i ++
}; // 正確, { 單獨作為一行
} 語句永遠單獨作為一行.如果 } 語句應該縮進到與其相對應的 { 那一行相對齊的位置。
括號
左括號和后一個字符之間不應該出現(xiàn)空格, 同樣, 右括號和前一個字符之間也不應該出現(xiàn)空格. 下面的例子說明括號和空格的錯誤及正確使用:
CallProc( AParameter ); // 錯誤
CallProc(AParameter); // 正確
不要在語句中使用無意義的括號. 括號只應該為達到某種目的而出現(xiàn)在源代碼中。下面的例子說明錯誤和正確的用法:
if ((I) = 42) { // 錯誤 - 括號毫無意義
if (I == 42) or (J == 42) then // 正確 - 的確需要括號
9、代碼編譯
1.編寫代碼時要注意隨時保存,并定期備份,防止由于斷電、硬盤損壞等原因造成代碼丟失。
2.同一項目組內,最好使用相同的編輯器,并使用相同的設置選項。
3.合理地設計軟件系統(tǒng)目錄,方便開發(fā)人員使用。
4.打開編譯器的所有告警開關對程序進行編譯。
5.在同一項目組或產品組中,要統(tǒng)一編譯開關選項。
6.使用工具軟件(如Visual SourceSafe)對代碼版本進行維護。如果大家有不明白的可以到安安DIY創(chuàng)作室留言。
10、可移植性
Borland Jbulider 不喜歡 synchronized 這個關鍵字,如果你的斷點設在這些關鍵字的作用域內的話,調試的時候你會發(fā)現(xiàn)的斷點會到處亂跳,讓你不知所措。除非必須,盡量不要使用。
換行
如果需要換行的話,盡量用 println 來代替在字符串中使用\n。
你不要這樣:
System.out.print(Hello,world!\n);
要這樣:
System.out.println(Hello,world!);
或者你構造一個帶換行符的字符串,至少要象這樣:
String newline = System.getProperty(line.separator);
System.out.println(Hello world + newline);
PrintStream
PrintStream 已經被不贊成(deprecated)使用,用 PrintWrite 來代替它。
另外一個回復是簡單的建議,限于代碼層面跟計算資源緊張的情況。
說說我的理論:
1、想要這么大的并發(fā)量,申請買幾臺比較好的機器,帶寬加到足夠大應該不是問題。
2、網絡層的負載均衡考慮一下,這應該也不是啥問題,這么大并發(fā)量,別告訴我客戶網絡設計很垃圾。高端點的網絡設備都沒有。
3、軟件層的結構,看用戶給什么樣的機器,如果經濟型的機器,考慮分布式運算,分布式再從軟件層面做集群或主從。根據計算密集型還是IO密集型選擇適合的設備。計算密集型的業(yè)務,需要強悍的CPU(可以用多C,未必是頻率高的)+內存。IO密集型的業(yè)務需要,速度快的存儲,越快越好。有極致要求的考慮固態(tài)存儲設備。
4、并發(fā)設計,java中主要是線程的應用。能拆分成多任務的運算一定拆成多任務。盡量壓榨cpu資源,現(xiàn)在的工作站不會寫并發(fā)程序,真浪費cpu資源。
5、數據庫并發(fā)的考量,以前說mysql不如oracle現(xiàn)在還有人有這論調。使用的時候把參數調整到最優(yōu)狀態(tài)。如緩存到內存中數據大小,內存要夠,就把全部表數據加載到內存中。mysql的innodb調整兩三個參數就行。數據庫IO訪問,mysql有測算IO能力的參數,存儲夠好,可以調整到很大。
6、這么大的并發(fā)量,選擇適合的緩存器,可以用來緩存熱點數據,或加快分布式運算的IO訪問能力。(磁盤肯定沒內存塊,包括固態(tài))
7、代碼編寫完后,對系統(tǒng)整體進行測試,評估,分析性能瓶頸,優(yōu)化代碼。
能做到以上標準,tomcat+mysql跑一堆集群也不會慢。
通過使用一些輔助性工具來找到程式中的瓶頸,然后就能對瓶頸部分的代碼進行優(yōu)化。一般有兩種方案:即優(yōu)化代碼或更改設計方法。我們一般會選擇后者,因為不去調用以下代碼要比調用一些優(yōu)化的代碼更能提高程式的性能。而一個設計良好的程式能夠精簡代碼,從而提高性能。
????下面將提供一些在JAVA程式的設計和編碼中,為了能夠提高JAVA程式的性能,而經常采用的一些方法和技巧。
????1.對象的生成和大小的調整。
????JAVA程式設計中一個普遍的問題就是沒有好好的利用JAVA語言本身提供的函數,從而常常會生成大量的對象(或實例)。由于系統(tǒng)不僅要花時間生成對象,以后可能還需花時間對這些對象進行垃圾回收和處理。因此,生成過多的對象將會給程式的性能帶來非常大的影響。
????例1:關于String ,StringBuffer,+和append
????JAVA語言提供了對于String類型變量的操作。但如果使用不當,會給程式的性能帶來影響。如下面的語句:
????String name=new String("HuangWeiFeng");
????System.out.println(name+"is my name");
????看似已非常精簡了,其實并非如此。為了生成二進制的代碼,要進行如下的步驟和操作:
????(1) 生成新的字符串 new String(STR_1);
????(2) 復制該字符串;
????(3) 加載字符串常量"HuangWeiFeng"(STR_2);
????(4) 調用字符串的構架器(Constructor);
????(5) 保存該字符串到數組中(從位置0開始);
????(6) 從java.io.PrintStream類中得到靜態(tài)的out變量;
????(7) 生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);
????(8) 復制該字符串緩沖變量;
????(9) 調用字符串緩沖的構架器(Constructor);
????(10) 保存該字符串緩沖到數組中(從位置1開始);
????(11) 以STR_1為參數,調用字符串緩沖(StringBuffer)類中的append方法;
????(12) 加載字符串常量"is my name"(STR_3);
????(13) 以STR_3為參數,調用字符串緩沖(StringBuffer)類中的append方法;
????(14) 對于STR_BUF_1執(zhí)行toString命令;
????(15) 調用out變量中的println方法,輸出結果。
????由此能看出,這兩行簡單的代碼,就生成了STR_1,STR_2,STR_3,STR_4和STR_BUF_1五個對象變量。這些生成的類的實例一般都存放在堆中。堆要對所有類的超類,類的實例進行初始化,同時還要調用類極其每個超類的構架器。而這些操作都是非常消耗系統(tǒng)資源的。因此,對對象的生成進行限制,是完全有必要的。
????經修改,上面的代碼能用如下的代碼來替換。
????StringBuffer name=new StringBuffer("HuangWeiFeng");
????System.out.println(name.append("is my name.").toString());
????系統(tǒng)將進行如下的操作:
????(1) 生成新的字符串緩沖變量new StringBuffer(STR_BUF_1);
????(2) 復制該字符串緩沖變量;
????(3) 加載字符串常量"HuangWeiFeng"(STR_1);
????(4) 調用字符串緩沖的構架器(Constructor);
????(5) 保存該字符串緩沖到數組中(從位置1開始);
????(6) 從java.io.PrintStream類中得到靜態(tài)的out變量;
????(7) 加載STR_BUF_1;
????(8) 加載字符串常量"is my name"(STR_2);
????(9) 以STR_2為參數,調用字符串緩沖(StringBuffer)實例中的append方法;
????(10) 對于STR_BUF_1執(zhí)行toString命令(STR_3);
????(11)調用out變量中的println方法,輸出結果。
????由此能看出,經過改進后的代碼只生成了四個對象變量:STR_1,STR_2,STR_3和STR_BUF_1.你可能覺得少生成一個對象不會對程式的性能有非常大的提高。但下面的代碼段2的執(zhí)行速度將是代碼段1的2倍。因為代碼段1生成了八個對象,而代碼段2只生成了四個對象。
????代碼段1:
????String name= new StringBuffer("HuangWeiFeng");
????name+="is my";
????name+="name";
????代碼段2:
????StringBuffer name=new StringBuffer("HuangWeiFeng");
????name.append("is my");
????name.append("name.").toString();
????因此,充分的利用JAVA提供的庫函數來優(yōu)化程式,對提高JAVA程式的性能時非常重要的.其注意點主要有如下幾方面;
????(1) 盡可能的使用靜態(tài)變量(Static Class Variables)
????如果類中的變量不會隨他的實例而變化,就能定義為靜態(tài)變量,從而使他所有的實例都共享這個變量。
????例:
????public class foo
????{
??????SomeObject so=new SomeObject();
????}
????就能定義為:
????public class foo
????{
??????static SomeObject so=new SomeObject();
????}
????(2) 不要對已生成的對象作過多的改動。
????對于一些類(如:String類)來講,寧愿在重新生成一個新的對象實例,而不應該修改已生成的對象實例。
????例:
????String name="Huang";
????name="Wei";
????name="Feng";
????上述代碼生成了三個String類型的對象實例。而前兩個馬上就需要系統(tǒng)進行垃圾回收處理。如果要對字符串進行連接的操作,性能將得更差,因為系統(tǒng)將不得為此生成更多得臨時變量,如上例1所示。
????(3) 生成對象時,要分配給他合理的空間和大小JAVA中的非常多類都有他的默認的空間分配大小。對于StringBuffer類來講,默認的分配空間大小是16個字符。如果在程式中使用StringBuffer的空間大小不是16個字符,那么就必須進行正確的初始化。
????(4) 避免生成不太使用或生命周期短的對象或變量。對于這種情況,因該定義一個對象緩沖池。以為管理一個對象緩沖池的開銷要比頻繁的生成和回收對象的開銷小的多。
????(5) 只在對象作用范圍內進行初始化。JAVA允許在代碼的所有地方定義和初始化對象。這樣,就能只在對象作用的范圍內進行初始化。從而節(jié)約系統(tǒng)的開銷。
????例:
????SomeObject so=new SomeObject();
????If(x==1) then
????{
??????Foo=so.getXX();
????}
????能修改為:
????if(x==1) then
????{
??????SomeObject so=new SomeObject();
??????Foo=so.getXX();
????}
????2.異常(Exceptions)
????JAVA語言中提供了try/catch來發(fā)方便用戶捕捉異常,進行異常的處理。不過如果使用不當,也會給JAVA程式的性能帶來影響。因此,要注意以下兩點:
????(1) 避免對應用程式的邏輯使用try/catch
????如果能用if,while等邏輯語句來處理,那么就盡可能的不用try/catch語句。
????(2) 重用異常
????在必須要進行異常的處理時,要盡可能的重用已存在的異常對象。以為在異常的處理中,生成一個異常對象要消耗掉大部分的時間。
????3. 線程(Threading)
????一個高性能的應用程式中一般都會用到線程。因為線程能充分利用系統(tǒng)的資源。在其他線程因為等待硬盤或網絡讀寫而 時,程式能繼續(xù)處理和運行。不過對線程運用不當,也會影響程式的性能。
????例2:正確使用Vector類
????Vector主要用來保存各種類型的對象(包括相同類型和不同類型的對象)。不過在一些情況下使用會給程式帶來性能上的影響。這主要是由Vector類的兩個特點所決定的。第一,Vector提供了線程的安全保護功能。即使Vector類中的許多方法同步。不過如果你已確認你的應用程式是單線程,這些方法的同步就完全不必要了。第二,在Vector查找存儲的各種對象時,常常要花非常多的時間進行類型的匹配。而當這些對象都是同一類型時,這些匹配就完全不必要了。因此,有必要設計一個單線程的,保存特定類型對象的類或集合來替代Vector類.用來替換的程式如下(StringVector.java):
????public class StringVector
????{
??????private String [] data;
??????private int count;
??????public StringVector()
??????{
????????this(10); // default size is 10
??????}
??????public StringVector(int initialSize)
??????{
????????data = new String[initialSize];
??????}
??????public void add(String str)
??????{
??????// ignore null strings
??????if(str == null) { return; }
??????ensureCapacity(count + 1);
??????data[count++] = str;
??????}
??????private void ensureCapacity(int minCapacity)
??????{
????????int oldCapacity = data.length;
????????if (minCapacity oldCapacity)
????????{
??????????String oldData[] = data;
??????????int newCapacity = oldCapacity * 2;
??????????data = new String[newCapacity];
??????????System.arraycopy(oldData, 0, data, 0, count);
????????}
??????}
??????public void remove(String str)
??????{
??????if(str == null) { return; // ignore null str }
??????for(int i = 0; i count; i++)
??????{
????????// check for a match
????????if(data[i].equals(str))
????????{
??????????System.arraycopy(data,i+1,data,i,count-1); // copy data
??????????// allow previously valid array element be gc?0?7d
??????????data[--count] = null;
??????????return;
????????}
??????}
??????}
??????public final String getStringAt(int index)
??????{
??????if(index 0) { return null; }
??????else if(index count) { return null; // index is # strings }
??????else { return data[index]; // index is good }
??????}
????}
????因此,代碼:
????Vector Strings=new Vector();
????Strings.add("One");
????Strings.add("Two");
????String Second=(String)Strings.elementAt(1);
????能用如下的代碼替換:
????StringVector Strings=new StringVector();
????Strings.add("One");
????Strings.add("Two");
????String Second=Strings.getStringAt(1);
????這樣就能通過優(yōu)化線程來提高JAVA程式的性能。用于測試的程式如下(TestCollection.java):
????import java.util.Vector;
????public class TestCollection
????{
??????public static void main(String args [])
??????{
????????TestCollection collect = new TestCollection();
????????if(args.length == 0)
????????{
??????????System.out.println("Usage: java TestCollection [ vector | stringvector ]");
??????????System.exit(1);
????????}
????????if(args[0].equals("vector"))
????????{
??????????Vector store = new Vector();
??????????long start = System.currentTimeMillis();
??????????for(int i = 0; i 1000000; i++)
??????????{
????????????store.addElement("string");
??????????}
??????????long finish = System.currentTimeMillis();
??????????System.out.println((finish-start));
??????????start = System.currentTimeMillis();
??????????for(int i = 0; i 1000000; i++)
??????????{
????????????String result = (String)store.elementAt(i);
??????????}
??????????finish = System.currentTimeMillis();
??????????System.out.println((finish-start));
????????}
????????else if(args[0].equals("stringvector"))
????????{
??????????StringVector store = new StringVector();
??????????long start = System.currentTimeMillis();
??????????for(int i = 0; i 1000000; i++) { store.add("string"); }
??????????long finish = System.currentTimeMillis();
??????????System.out.println((finish-start));
??????????start = System.currentTimeMillis();
??????????for(int i = 0; i 1000000; i++) {
????????????String result = store.getStringAt(i);
??????????}
??????????finish = System.currentTimeMillis();
??????????System.out.println((finish-start));
????????}
??????}
????}
????關于線程的操作,要注意如下幾個方面:
????(1) 防止過多的同步
????如上所示,不必要的同步常常會造成程式性能的下降。因此,如果程式是單線程,則一定不要使用同步。
????(2) 同步方法而不要同步整個代碼段
????對某個方法或函數進行同步比對整個代碼段進行同步的性能要好。
????(3) 對每個對象使用多”鎖”的機制來增大并發(fā)。
????一般每個對象都只有一個”鎖”,這就表明如果兩個線程執(zhí)行一個對象的兩個不同的同步方法時,會發(fā)生”死鎖”。即使這兩個方法并不共享所有資源。為了避免這個問題,能對一個對象實行”多鎖”的機制。如下所示:
????class foo
????{
??????private static int var1;
??????private static Object lock1=new Object();
??????private static int var2;
??????private static Object lock2=new Object();
??????public static void increment1()
??????{
????????synchronized(lock1)
????????{
??????????var1++;
????????}
??????}
??????public static void increment2()
??????{
????????synchronized(lock2)
????????{
??????????var2++;
????????}
??????}
????}
????4.輸入和輸出(I/O)
????輸入和輸出包括非常多方面,但涉及最多的是對硬盤,網絡或數據庫的讀寫操作。對于讀寫操作,又分為有緩存和沒有緩存的;對于數據庫的操作,又能有多種類型的JDBC驅動器能選擇。但無論怎樣,都會給程式的性能帶來影響。因此,需要注意如下幾點:
????(1) 使用輸入輸出緩沖
????盡可能的多使用緩存。但如果要經常對緩存進行刷新(flush),則建議不要使用緩存。
????(2) 輸出流(Output Stream)和Unicode字符串
當時用Output Stream和Unicode字符串時,Write類的開銷比較大。因為他要實現(xiàn)Unicode到字節(jié)(byte)的轉換.因此,如果可能的話,在使用Write類之前就實現(xiàn)轉換或用OutputStream類代替Writer類來使用。
????(3) 當需序列化時使用transient
當序列化一個類或對象時,對于那些原子類型(atomic)或能重建的原素要表識為transient類型。這樣就不用每一次都進行序列化。如果這些序列化的對象要在網絡上傳輸,這一小小的改動對性能會有非常大的提高。
????(4) 使用高速緩存(Cache)
對于那些經常要使用而又不大變化的對象或數據,能把他存儲在高速緩存中。這樣就能提高訪問的速度。這一點對于從數據庫中返回的結果集尤其重要。
????(5) 使用速度快的JDBC驅動器(Driver)
JAVA對訪問數據庫提供了四種方法。這其中有兩種是JDBC驅動器。一種是用JAVA外包的本地驅動器;另一種是完全的JAVA驅動器。具體要使用哪一種得根據JAVA布署的環(huán)境和應用程式本身來定。
????5.一些其他的經驗和技巧
????(1) 使用局部變量。
????(2) 避免在同一個類中動過調用函數或方法(get或set)來設置或調用變量。
????(3) 避免在循環(huán)中生成同一個變量或調用同一個函數(參數變量也相同)。
????(4) 盡可能的使用static,final,private等關鍵字。
java是解釋性語言,它不像C++那樣直接被譯為可執(zhí)行的機器代碼運行。而是通過虛擬機運行字節(jié)碼。也就是語言運行的時候有間接性。
為什么要有虛擬機呢?因為java最重要的優(yōu)勢和特點之一就是可移植性。同樣的字節(jié)碼,配上不同的虛擬機,就能在單片機啊,PC啊,服務器等各種操作系統(tǒng)和硬件環(huán)境下運行。所以犧牲一點點性能是值得的。
并且,隨著現(xiàn)在硬件水平的提高,java的性能瓶頸也被越來越縮小,所以不能說java運行速度很慢,只是適用的情況不同罷了。在大型網站,平臺等的背后,java可以發(fā)揮它的用武之地,并且保持讓人滿意的性能。
大多數JAVA開發(fā)人員理所當然地以為性能優(yōu)化很復雜,需要大量的經驗和知識。
好吧,不能說這是完全錯誤的。
優(yōu)化應用程序以獲得最佳性能不是一件容易的事情。
但是,這并不意味著如果你不具備這些知識,就不能做任何事情。
這里有一些易于遵循的調優(yōu)方式,青島java培訓建議可以做個參考!??大部分建議是針對Java的。
但也有若干建議是與語言無關的,可以應用于所有應用程序和編程語言。
在討論專門針對Java的性能調優(yōu)技巧之前,讓我們先來看看通用技巧。
1.在你知道必要之前不要優(yōu)化??這可能是最重要的性能調整技巧之一。
你應該遵循常見的最佳實踐做法并嘗試高效地實現(xiàn)用例。
但是,這并不意味著在你證明必要之前,你應該更換任何標準庫或構建復雜的優(yōu)化。
在大多數情況下,過早優(yōu)化不但會占用大量時間,而且會使代碼變得難以閱讀和維護。
更糟糕的是,這些優(yōu)化通常不會帶來任何好處,因為你花費大量時間來優(yōu)化的是應用程序的非關鍵部分。
那么,你如何證明你需要優(yōu)化一些東西呢???首先,你需要定義應用程序代碼的速度得多快,例如,為所有API調用指定最大響應時間,或者指定在特定時間范圍內要導入的記錄數量。
在完成這些之后,你就可以測量應用程序的哪些部分太慢需要改進。
然后,接著看第二個技巧。
2.使用分析器查找真正的瓶頸??在你遵循第一個建議并確定了應用程序的某些部分需要改進后,那么從哪里開始呢???你可以用兩種方法來解決問題:??·查看你的代碼,并從看起來可疑或者你覺得可能會產生問題的部分開始。
·或者使用分析器并獲取有關代碼每個部分的行為和性能的詳細信息。
希望不需要我解釋為什么應該始終遵循第二種方法的原因。
很明顯,基于分析器的方法可以讓你更好地理解代碼的性能影響,并使你能夠專注于最關鍵的部分。
如果你曾使用過分析器,那么你一定記得曾經你是多么驚訝于一下就找到了代碼的哪些部分產生了性能問題。
老實說,我第一次的猜測不止一次地導致我走錯了方向。
3.為整個應用程序創(chuàng)建性能測試套件??這是另一個通用技巧,可以幫助你避免在將性能改進部署到生產后經常會發(fā)生的許多意外問題。
你應該總是定義一個測試整個應用程序的性能測試套件,并在性能改進之前和之后運行它。
這些額外的測試運行將幫助你識別更改的功能和性能副作用,并確保不會導致弊大于利的更新。
如果你工作于被應用程序若干不同部分使用的組件,如數據庫或緩存,那么這一點就尤其重要。