JVM.dll裝載過程與源代碼分析
你知道JVM.dll轉載過程嗎,這里和大家分享一下,首先看一下它的概念,JVM.dll是一個包含可由多個程序同時使用的代碼和數據的庫。例如,在Windows操作系統中,Comdlg32DLL執行與對話框有關的常見函數。
淺談JVM.dll裝載過程與源代碼分析
眾所周知java.exe是javaclass文件的執行程序,但實際上java.exe程序只是一個執行的外殼,它會裝載JVM.dll,這個動態連接庫才是java虛擬機的實際操作處理所在。本文探究java.exe程序是如何查找和裝載JVM.dll動態庫,并調用它進行class文件執行處理的。
JVM.dll源代碼
本文分析之代碼,《JavaTM2SDK,StandardEdition,v1.4.2fcsCommunitySourceRelease》,可從sun官方網站下載,主要分析的源代碼為:j2se\src\share\bin\java.cj2se\src\windows\bin\java_md.c
java.c是什么東西
‘java程序’源代碼所謂‘java程序’,包括jdk中的java.exe\javac.exe\javadoc.exe,java.c源代碼中通過JAVA_ARGS宏來控制生成的代碼,如果該宏沒定義則編譯文件控制生成java.exe否則編譯文件控制生成其他的‘java程序’。比如:j2se\make\java\javac\Makefile(這是javac編譯文件)中:$(CD)../../sun/javac;$(MAKE)$@RELEASE=$(RELEASE)FULL_VERSION=$(FULL_VERSION)j2se\make\sun\javac\javac\Makefile(由上面Makefile文件調用)中:JAVA_ARGS="{\"-J-ms8m\",\"com.sun.tools.javac.Main\"}"則由同一份java.c代碼生成的javac.exe程序就會直接調用java類方法:com.sun.tools.javac.Main,這樣使其執行起來就像是直接運行的一個exe文件,而未定義JAVA_ARGS的java.exe程序則會調用傳遞過來參數中的類方法。
從java.c的main入口函數說起
main()函數中前面一段為重新分配參數指針的處理。然后調用函數:CreateExecutionEnvironment,該函數主要查找java運行環境的目錄,和JVM.dll這個虛擬機核心動態連接庫文件路徑所在。根據操作系統不同,該函數有不同實現版本,但大體處理邏輯相同,我們看看windows平臺該函數的處理(j2se\src\windows\bin\java_md.c)。
CreateExecutionEnvironment函數主要分為三步處理:a、查找jre路徑。b、裝載jvm.cfg中指定的虛擬機動態連接庫(JVM.dll)參數。c、取JVM.dll文件路徑。
實現:
◆a、查找jre路徑是通過java_md.c中函數:GetJREPath實現的。
該函數首先調用GetApplicationHome函數,GetApplicationHome函數調用windowsAPI函數GetModuleFileName取java.exe程序的絕對路徑,以我的jdk安裝路徑為例,為:“D:\java\j2sdk1.4.2_04\bin\java.exe”,然后去掉文件名取絕對路徑為:“D:\java\j2sdk1.4.2_04\bin”,之后會在去掉***一級目錄,現在絕對路徑為:“D:\java\j2sdk1.4.2_04”。然后GetJREPath函數繼續判斷剛剛取的路徑+\bin\java.dll組合成的這個java.dll文件是否存在,如果存在則“D:\java\j2sdk1.4.2_04”為JRE路徑,否則判斷取得的“D:\java\j2sdk1.4.2_04”路徑+\jre\bin\java.dll文件是否存在,存在則“D:\java\j2sdk1.4.2_04\jre”為JRE路徑。如果上面兩種情況都不存在,則從注冊表中去查找(參見函數GetPublicJREHome)。
函數:GetPublicJREHome先查找HKEY_LOCAL_MACHINE\Software\JavaSoft\JavaRuntimeEnvironment\CurrentVersion鍵值“當前JRE版本號”,判斷“當前JRE版本號”是否為1.4做為版本號,如果是則取HKEY_LOCAL_MACHINE\Software\JavaSoft\JavaRuntimeEnvironment\“當前JRE版本號”\JavaHome的路徑所在為JRE路徑。
我的JDK返回的JRE路徑為:“D:\java\j2sdk1.4.2_04\jre”。#p#
◆b、裝載jvm.cfg虛擬機動態連接庫配置文件是通過java.c中函數:ReadKnownVMs實現的。
該函數首先組合jvm.cfg文件的絕對路徑,JRE路徑+\lib+\ARCH(CPU構架)+\jvm.cfgARCH(CPU構架)的判斷是通過java_md.c中GetArch函數判斷的,該函數中windows平臺只有兩種情況:WIN64的‘ia64’,其他情況都為‘i386’。我的為i386所以jvm.cfg文件絕對路徑為:“D:\java\j2sdk1.4.2_04\jre\lib\i386\jvm.cfg”。文件內容如下:
- ##@(#)jvm.cfg 1.703/01/23##Copyright2003SunMicrosystems,
- Inc.Allrightsreserved.
- #SUNPROPRIETARY/CONFIDENTIAL.Useissubjecttolicenseterms.
- #####ListofJVMsthatcanbeusedasanoptiontojava,javac,etc.
- #Orderisimportant--irstinthislististhedefaultJVM.
- #NOTEthatthisboththisfileanditsformatareUNSUPPORTEDand
- #WILLGOAWAYinafuturerelease.
- ##YoumayalsoselectaJVMinanarbitrarylocationwiththe
- #"-XXaltjvm="option,butthattooisunsupported
- #andmaynotbeavailableinafuturerelease.
- #-clientKNOWN-serverKNOWN-hotspotALIASED_TO-client-
- classicWARN-nativeERROR-greenERROR
(如果細心的話,我們會發現在JDK目錄中我的為:“D:\java\j2sdk1.4.2_04\jre\bin\client”和“D:\java\j2sdk1.4.2_04\jre\bin\server”兩個目錄下都存在JVM.dll文件。而java正是通過jvm.cfg配置文件來管理這些不同版本的JVM.dll的。)
ReadKnownVMs函數會將該文件中的配置內容讀入到一個JVM配置結構的全局變量中,該函數首先跳過注釋(以‘#’開始的行),然后讀取以‘-’開始的行指定的jvm參數,每一行為一個jvm信息,***部分為jvm虛擬機名稱,第二部分為配置參數,比如行:“-clientKNOWN”則“-client”為虛擬機名稱,而“KNOWN”為配置類型參數,“KNOWN”表示該虛擬機的JVM.dll存在,而“ALIASED_TO”表示為另一個JVM.dll的別名,“WARN”表示該虛擬機的JVM.dll不存在但運行時會用其他存在的JVM.dll替代執行,而“ERROR”同樣表示該類虛擬機的JVM.dll不存在且運行時不會找存在的JVM.dll替代而直接拋出錯誤信息。
在運行java程序時指定使用那個虛擬機的判斷是由java.c中函數:CheckJvmType判斷,該函數會檢查java運行參數中是否有指定jvm的參數,然后從ReadKnownVMs函數讀取的jvm.cfg數據結構中去查找,從而指定不同的jvm類型(最終導致裝載不同JVM.dll)。有兩種方法可以指定jvm類型,一種按照jvm.cfg文件中的jvm名稱指定,第二種方法是直接指定,它們執行的方法分別是“java-J”、“java-XXaltjvm=”或“java-J-XXaltjvm=”。如果是***種參數傳遞方式,CheckJvmType函數會取參數‘-J’后面的jvm名稱,然后從已知的jvm配置參數中查找如果找到同名的則去掉該jvm名稱前的‘-’直接返回該值;而第二種方法,會直接返回“-XXaltjvm=”或“-J-XXaltjvm=”后面的jvm類型名稱;如果在運行java時未指定上面兩種方法中的任一一種參數,CheckJvmType會取配置文件中***個配置中的jvm名稱,去掉名稱前面的‘-’返回該值。CheckJvmType函數的這個返回值會在下面的函數中匯同jre路徑組合成JVM.dll的絕對路徑。
比如:如果在運行java程序時使用“java-J-clienttest”則ReadKnownVMs會讀取參數“-client”然后查找jvm.cfg讀入的參數中是否有jvm名稱為“-client”的,如果有則去掉jvm名稱前的“-”直接返回“client”;而如果在運行java程序時使用如下參數:“java-XXaltjvm=D:\java\j2sdk1.4.2_04\jre\bin\clienttest”,則ReadKnownVMs會直接返回“D:\java\j2sdk1.4.2_04\jre\bin\client”;如果不帶上面參數執行如:“javatest”,因為在jvm.cfg配置文件中***個存在的jvm為“-client”,所以函數ReadKnownVMs也會去掉jvm名稱前的“-”返回“client”。其實這三中情況都是使用的“D:\java\j2sdk1.4.2_04\jre\bin\client\JVM.dll”這個jvm動態連接庫處理test這個class的,見下面GetJVMPath函數。
◆c、取JVM.dll文件路徑是通過java_md.c中函數:GetJVMPath實現的。
由上面兩步我們已經獲得了JRE路徑和jvm的類型字符串。GetJVMPath函數判斷CheckJvmType返回的jvm類型字符串中是否包含了‘\’或‘/’如果包含則以該jvm類型字符串+\JVM.dll作為JVM的全路徑,否則以JRE路徑+\bin+\jvm類型字符串+\JVM.dll作為JVM的全路徑。
看看上面的例子,***種情況“java-J-clienttest”JVM.dll路徑為:JRE路徑+\bin+\jvm類型字符串+\JVM.dll按照我的JDK路徑則為:“D:\java\j2sdk1.4.2_04\jre”+“\bin”+“\client”+“\JVM.dll”。第二種情況“java-XXaltjvm=D:\java\j2sdk1.4.2_04\jre\bin\clienttest”路徑為:jvm類型字符串+\JVM.dll即為:“D:\java\j2sdk1.4.2_04\jre\bin\client”+“\JVM.dll”第三種情況“javatest”為:“D:\java\j2sdk1.4.2_04\jre”+“\bin”+“\client”+“\JVM.dll”與情況一相同。所以這三種情況都是調用的jvm動態連接庫“D:\javaj2sdk1.4.2_04\jre\bin\client\JVM.dll”處理test類的。
我們來進一步驗證一下:打開cmd控制臺:
設置java裝載調試E:\work\java_research>set_JAVA_LAUNCHER_DEBUG=1
情況一E:\work\java_research>java-J-clienttest.ScanDirectory----_JAVA_LAUNCHER_DEBUG----
【編輯推薦】
- JVM.DLL文件用法詳解
- 淺談jvm.dll裝載過程與源代碼分析
- Java編譯過程與c/c++編譯過程有何不同
- Java虛擬機發展回顧 為跨平臺而生
- 淺析安裝Tomcat的jvm.dll問題及其解決方法