top
Loading...
編寫高級應用程序
新的JavaTM 虛擬機(VMs)具有能夠提高性能的特點, 并且你可以使用許多工具來提高應用程序的性能或減小一般類文件的尺寸。這種Java虛擬機的特性和工具可使你在不改變應用程序、或對應用程序僅做很小改動的情況下, 提高應用程序的性能。

Java虛擬機的特性


Java2與過去的版本相比, 性能已有很大提高, 其中包括更快的內存分配、類尺寸的減小、垃圾收集的改善、最新型的監控器和作為標準的內聯JIT技術。當使用新的Java2虛擬機時, 你會看到這種性能的改善; 然而, 如果你能夠理解速度是如何提高的, 你就能夠調整你的應用程序, 以充分挖掘每一點性能潛力。
方法內聯
Java虛擬機的Java2版可在運行時自動內聯簡單方法。在一個未優化的Java虛擬機中,每調用一次新的方法,就創建一個新的堆棧幀(stack frame)。創建一個新的堆棧幀需要一些額外的資源以及該棧的某些再映射(re-mapping),其結果將導致系統開銷的少許增加。


由于方法內聯可在你的程序中減少方法調用的次數,因而可提高性能。Java虛擬機內聯代碼內聯了返回常數或僅訪問內部域(internal fields)的方法。為了利用方法內聯,你可以從以下兩件事中選做其一;即:你可以使一個方法對虛擬機所要執行的內聯看上去是有吸引力的,或者你可以手工內聯一個方法,只要它不破壞你的對象模型。在這一語境中的手工內聯意味著可以直接從一個方法中將代碼移動到正在調用該方法的方法中。


下面的小例子演示了虛擬機的自動方法內聯:
public class InlineMe {

int counter=0;

public void method1() {
for(int i=0;i$#@60;1000;i++)
addCount();
System.out.println("counter="+counter);
}

public int addCount() {
counter=counter+1;
return counter;
}

public static void main(String args[]) {
InlineMe im=new InlineMe();
im.method1();
}
}


在當前狀態,addCount方法對虛擬機中的內聯探測器顯得是沒有吸引力的,因為addCount方法返回一個值。要發現該方法是否被內聯:

java -Xrunhprof:cpu=times InlineMe

它生成一個java.hprof.txt輸出文件。前十個方法類似下面的結果:

CPU TIME (ms) BEGIN (total = 510) Thu Jan 28 16:56:15 1999
rank self acc count trace method
1 5.88% 5.88% 1 25 java/lang/Character.
2 3.92% 9.80% 5808 13 java/lang/String.charAt
3 3.92% 13.73% 1 33 sun/misc/Launcher$AppClassLoader.getPermissions
4 3.92% 17.65% 3 31 sun/misc/URLClassPath.getLoader
5 1.96% 19.61% 1 39 java/net/URLClassLoader.access$1
6 1.96% 21.57% 1000 46 InlineMe.addCount
7 1.96% 23.53% 1 21 sun/io/Converters.newConverter
8 1.96% 25.49% 1 17 sun/misc/Launcher$ExtClassLoader.getExtDirs
9 1.96% 27.45% 1 49 java/util/Stack.peek
10 1.96% 29.41% 1 24 sun/misc/Launcher.

如果你將addCount方法改變為不再返回一個值,則虛擬機可在運行時將其內聯。為使內聯代碼更友好,應用下面的程序替換addCount方法:

public void addCount() {
counter=counter+1;
}

再次運行profiler:

java -Xrunhprof:cpu=times InlineMe

這次,java.hprof.txt的輸出應該顯得是不同的。
AddCount方法已經消失。它已被內聯了!

CPU TIME (ms) BEGIN (total = 560) Thu Jan 28 16:57:02 1999
rank self accum count trace method
1 5.36% 5.36% 1 27 java/lang/Character.
2 3.57% 8.93% 1 23 java/lang/System.initializeSystemClass
3 3.57% 12.50% 2 47 java/io/PrintStream.
4 3.57% 16.07% 5808 15 java/lang/String.charAt
5 3.57% 19.64% 1 42 sun/net/www/protocol/file/Handler.openConnection
6 1.79% 21.43% 2 21 java/io/InputStreamReader.fill
7 1.79% 23.21% 1 54 java/lang/Thread.
8 1.79% 25.00% 1 39 java/io/PrintStream.write
9 1.79% 26.79% 1 40 java/util/jar/JarFile.getJarEntry
10 1.79% 28.57% 1 38 java/lang/Class.forName0

新型同步


在Java 2發布之前,同步的方法和對象總是引發一些額外的性能干擾,這是因為用來實現這種代碼鎖定的機制采用了一種全局監控器注冊,它在某些區域僅僅是單線程的(如搜索現存監控器)。在新發布的Java 2中,每個線程都有一個監控器注冊,從而消除了許多現存的性能瓶頸。


如果你曾經使用過其它鎖定機制來避免同步方法的性能干擾,現在則有必要重訪這些代碼并考慮新的Java 2新型鎖定技術。


在下面的為同步塊創建監控器的例子中,你可以將速度提高40%。所用時間在采用JDK1.1.7和采用Sun Ultra 1上的Java 2時分別為14ms和10ms。

class MyLock {

static Integer count=new Integer(5);
int test=0;

public void letslock() {
synchronized(count) {
test++;
}
}
}

public class LockTest {

public static void main(String args[]) {

MyLock ml=new MyLock();
long time = System.currentTimeMillis();

for(int i=0;i$#@60;5000;i++ ) {
ml.letslock();
}
System.out.println("Time taken="+
(System.currentTimeMillis()-time));
}
}

Java Hotspot


Java HotSpotTM虛擬機是Sun Microsystem公司的下一代虛擬機。雖然Java HotSpot 虛擬機所采用的規范與Java 2虛擬機所采用的規范相同,但它已被重新設計,并使用了最先進的技術,從而可在未來許多年內,能夠為Java平臺提供一個強大而可靠的性能引擎。Java HotSpot虛擬機可提供:
可以探測并加速性能關鍵性代碼的實時動態優化技術。
為發揮線程的最大性能而設計的超快速線程同步。
可最快速獲取的精確而可靠的垃圾收集器。
由于其簡潔、高層次以及面向對象的設計,因而在可維護性和可擴展性方面的重要改進。
JIT(Just-In-Time)編譯器
用來提高應用程序性能的最簡單的工具是Just-In-Time(JIT)實時編譯器。JIT是一個可將Java字節碼轉換為本地機器碼的代碼生成器。由JIT調用的Java程序,其運行速度通常要比由解釋程序執行字節碼時的速度高得多。


JIT編譯器首先是在Java開發工具包(JDKTM)1.1.6中作為一種性能更新出現的,而現在它是你在Java 2平臺上使用Java解釋程序命令時調用的標準工具。你可以使用Java虛擬機的-Djava.compiler=NONE 選項來使JIT編譯器失效,這在JIT的末尾部分有更詳細的闡述。

JIT編譯器是如何工作的?


JIT編譯器是作為一種依賴于平臺的本地庫提供的。如果JIT編譯器庫存在,則Java虛擬機將初始化Java本地接口(JNI)的本地代碼分支以調用在該庫中可獲得的JIT函數,而不是調用在解釋程序中的相應函數。


java.lang.Compiler 類被用來加載本地庫并啟動JIT編譯器內的初始化。當Java虛擬機調用一個Java方法時,它使用在加載的類對象的方法塊中所指定的調用(invoker)方法。Java虛擬機具有若干個調用者方法,例如,如果方法是同步的,或者它是一個本地方法,則將使用不同的調用者。JIT編譯器使用它自己的調用者。Sun的產品可以為值ACC_MACHINE_COMPILED檢查方法存取位以告知解釋程序該方法的代碼已被編譯并存儲在加載類中。

代碼何時成為JIT編譯的代碼?


當一個方法被首次調用時,JIT編譯器為該方法將方法塊編譯為本地代碼,并將其存儲在該方法的代碼塊中。


一旦代碼被編譯完成,在Sun平臺上所使用的ACC_MACHINE_COMPILED的位則被設定。

我如何知道JIT編譯器在做什么?


環境變量JIT_ARGS允許對Sun Solaris JIT編譯器進行簡單控制。trace 和 exclude(list)是兩個有用的值。要從示例InlineMe中排除(exclude)方法并顯示跟蹤記錄(trace),應將JIT_ARGS 做如下設定:

Unix:
export JIT_ARGS="trace exclude(InlineMe.addCount InlineMe.method1)"

$ java InlineMe
Initializing the JIT library ...
DYNAMICALLY COMPILING java/lang/System.getProperty mb=0x63e74
DYNAMICALLY COMPILING java/util/Properties.getProperty mb=0x6de74
DYNAMICALLY COMPILING java/util/Hashtable.get mb=0x714ec
DYNAMICALLY COMPILING java/lang/String.hashCode mb=0x44aec
DYNAMICALLY COMPILING java/lang/String.equals mb=0x447f8
DYNAMICALLY COMPILING java/lang/String.valueOf mb=0x454c4
DYNAMICALLY COMPILING java/lang/String.toString mb=0x451d0
DYNAMICALLY COMPILING java/lang/StringBuffer. mb=0x7d690
$#@62;$#@62;$#@62;$#@62; Inlined java/lang/String.length (4)


請注意內聯方法(如String.length)是免除的。String.length 也是一個特殊的方法,它一般由Java解釋程序編譯為一個內部快捷字節碼。當使用JIT編譯器時,由Java解釋程序提供的這些優化失效,從而可以使JIT編譯器能夠理解哪個方法正在被調用。


如何使用JIT來發揮你的優勢


首先要記住的一點是,JIT編譯器在第二次調用一個方法時,會獲得大部分速度上的改善。JIT編譯器的確是編譯了整個方法,而不是對其進行逐行解釋,逐行解釋也是一種在運行一個可執行JIT的應用程序時用以改善性能的途徑。這意味著如果代碼僅被調用一次,你將不會看到太大的性能改善。JIT編譯器也將忽略構造函數(class constructor),所以,如果可能的話,應最少量地保留構造函數代碼。


如果不能預先檢查某些Java邊界條件,JIT編譯器也不能獲得最大的性能改善,這些邊界條件包括零指針(Null pointer)或邊界外數組等異常。JIT編譯器能夠知道出現零指針異常的唯一途徑是通過由操作系統所提供的信號。由于該信號來自操作系統,而不是來自Java虛擬機,因而你的程序會出現性能上的干擾。為了保證在用JIT運行一個應用程序時,能夠獲取最好的性能,應確保你的代碼完全沒有象零指針或邊界外數組那樣的異常。


如果你要以遠程調試狀態運行Java虛擬機,或者你要看到源代碼行數而不是看到在你的Java棧跟蹤中的標簽(Compiled Code)的話,你可能需要使JIT編譯器失效。要使JIT編譯器失效,可在你調用解釋器命令時為JIT編譯器提供一個空白或無效名稱。下面的例子演示了用javac命令將源代碼編譯為字節碼、以及用兩種形式的java命令在沒有JIT編譯器的情況下調用解釋程序的過程。
javac MyClass.java
java -Djava.compiler=NONE MyClass
or
javac MyClass.java
java -Djava.compiler="" MyClass

第三方工具


其它一些可用的工具包括可用來減小一般Java類文件尺寸的工具。Java類文件包括一個被稱作常數池(constant pool)的區域。常數池在某一個地方為類文件保持有一個字符串和其它信息的列表,以備引用。在常數池中可以獲取的諸多信息之一是方法和字段的名稱。


類文件引用在類中的一個字段作為對常數池中的一個條目的引用。這意味著只要引用保持相同,則無所謂在常數池中存儲什么樣的值。 一些工具利用這點將常數池中的字段名和方法名重寫為縮短的名稱。利用這一技術可以大大減小類文件的尺寸,從而使在網上下載類文件變得簡單。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗