深刻解析JVM對dll文件和對類的裝載進程。本站提示廣大學習愛好者:(深刻解析JVM對dll文件和對類的裝載進程)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析JVM對dll文件和對類的裝載進程正文
JVM的對dll文件的裝載進程
操作體系裝入JVM是經由過程jdk中Java.exe來完成,經由過程上面4步來完成JVM情況.
1.創立JVM裝載情況和設置裝備擺設
2.裝載JVM.dll
3.初始化JVM.dll並掛界到JNIENV(JNI挪用接口)實例
4.挪用JNIEnv實例裝載並處置class類。
一.JVM裝入情況,JVM供給的方法是操作體系的靜態銜接文件.
既然是文件那就一個裝入途徑的成績,Java是怎樣找這個途徑的呢?當你在挪用Java test的時刻,操作體系會在path下在你的Java.exe法式,Java.exe就經由過程上面一個進程來肯定JVM的途徑和相干的參數設置裝備擺設了.上面基於Windows的完成的剖析.
起首查找jre途徑,Java是經由過程GetApplicationHome api【該辦法存在於java_md.c】來取得以後的Java.exe相對途徑。
例如 c:\j2sdk1.4.2_09\bin\Java.exe,那末它會截取到相對途徑c:\j2sdk1.4.2_09\,斷定c:\j2sdk1.4.2_09\bin\Java.dll文件能否存在,假如存在就把c:\j2sdk1.4.2_09\作為jre途徑,假如不存在則斷定c:\j2sdk1.4.2_09\jre\bin\Java.dll能否存在,假如存在這c:\j2sdk1.4.2_09\jre作為jre途徑.假如不存在挪用GetPublicJREHome查HKEY_LOCAL_MACHINE\Software\JavaSoft\Java Runtime Environment\“以後JRE版本號”\JavaHome的途徑為jre途徑。
備注獲得以後jre途徑的辦法為System.out.println(System.getProperty("java.home"));
然後裝載JVM.cfg文件JRE途徑+\lib+\ARCH(CPU構架)+\JVM.cfgARCH(CPU構架)的斷定是經由過程Java_md.c中GetArch函數斷定的,該函數中windows平台只要兩種情形:WIN64的‘ia64',其他情形都為‘i386'。以我的為例:C:\j2sdk1.4.2_09\jre\lib\i386\JVM.cfg.重要的內容以下:
在我們的jdk目次中jre\bin\server和jre\bin\client都有JVM.dll文件存在,而Java恰是經由過程JVM.cfg設置裝備擺設文件來治理這些分歧版本的JVM.dll的.經由過程文件我們可以界說今朝jdk中支撐那些JVM,後面部門(client)是JVM稱號,前面是參數,KNOWN表現JVM存在,ALIASED_TO表現給其余JVM取一個體名,WARN表現不存在時找一個JVM替換,ERROR表現不存在拋出異常.
在運轉Java XXX是,Java.exe會經由過程CheckJVMType來檢討以後的JVM類型,Java可以經由過程兩種方法來指定詳細的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的相對途徑。
假如沒有指定這會應用JVM.cfg中第一個界說的JVM.可以經由過程set _Java_LAUNCHER_DEBUG=1在掌握台上測試.
最初取得JVM.dll的途徑,JRE途徑+\bin+\JVM類型字符串+\JVM.dll就是JVM的文件途徑了,然則假如在挪用Java法式時用-XXaltJVM=參數指定的途徑path,就直接用path+\JVM.dll文件做為JVM.dll的文件途徑.
二:裝載JVM.dll
經由過程第一步曾經找到了JVM的途徑,Java經由過程LoadJavaVM來裝入JVM.dll文件.裝入任務很簡略就是挪用Windows API函數:
LoadLibrary裝載JVM.dll靜態銜接庫.然後把JVM.dll中的導出函數JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs掛接到 InvocationFunctions變量的CreateJavaVM和GetDefaultJavaVMInitArgs函數指針變量上。JVM.dll的裝載任務宣布完成。
三:初始化JVM,取得當地挪用接口
如許便可以在Java中挪用JVM的函數了.挪用InvocationFunctions->CreateJavaVM也就是JVM中 JNI_CreateJavaVM辦法取得JNIEnv構造的實例.
四:運轉Java法式.
Java法式有兩種方法一種是jar包,一種是class. 運轉jar,Java -jar XXX.jar運轉的時刻,Java.exe挪用GetMainClassName函數,該函數先取得JNIEnv實例然後挪用Java類Java.util.jar.JarFileJNIEnv中辦法getManifest()並從前往的Manifest對象中取getAttributes("Main-Class")的值即jar包中文件:META-INF/MANIFEST.MF指定的Main-Class的主類名作為運轉的主類。以後main函數會挪用Java.c中LoadClass辦法裝載該主類(應用JNIEnv實例的FindClass)。main函數直接挪用Java.c中LoadClass辦法裝載該類。假如是履行class辦法。main函數直接挪用Java.c中LoadClass辦法裝載該類。
然後main函數挪用JNIEnv實例的GetStaticMethodID辦法查找裝載的class主類中。“public static void main(String[] args)”辦法,並斷定該辦法能否為public辦法,然後挪用JNIEnv實例的 CallStaticVoidMethod辦法挪用該Java類的main辦法。
JVM的類裝載
1、引言
Java虛擬機(JVM)的類裝載就是指將包括在類文件中的字節碼裝載到JVM中, 並使其成為JVM一部門的進程。JVM的類靜態裝載技巧可以或許在運轉時辰靜態地加載或許調換體系的某些功效模塊, 而不影響體系其他功效模塊的正常運轉。本文將剖析JVM中的類裝載體系,商量JVM中類裝載的道理、完成和運用。
2、Java虛擬機的類裝載完成與運用
2.1 裝載進程簡介
所謂裝載就是尋覓一個類或是一個接口的二進制情勢並用該二進制情勢來結構代表這個類或是這個接口的class對象的進程,個中類或接口的稱號是給定了的。固然稱號也能夠經由過程盤算獲得,然則更罕見的是經由過程搜刮源代碼經由編譯器編譯後所獲得的二進制情勢來結構。
在Java中,類裝載器把一個類裝入Java虛擬機中,要經由三個步調來完成:裝載、鏈接和初始化,個中鏈接又可以分紅校驗、預備息爭析三步,除解析外,其它步調是嚴厲依照次序完成的,各個步調的重要任務以下:
2.2 裝載的完成
JVM中類的裝載是由ClassLoader和它的子類來完成的,Java ClassLoader 是一個主要的Java運轉時體系組件。它擔任在運轉時查找和裝入類文件的類。
在Java中,ClassLoader是一個籠統類,它在包java.lang中,可以如許說,只需懂得了在ClassLoader中的一些主要的辦法,再聯合下面所引見的JVM中類裝載的詳細的進程,對靜態裝載類這項技巧就有了一個比擬年夜概的控制,這些主要的辦法包含以下幾個:
①loadCass辦法 loadClass(String name ,boolean resolve)個中name參數指定了JVM須要的類的稱號,該稱號以包表現法表現,如Java.lang.Object;resolve參數告知辦法能否須要解析類,在初始化類之前,應斟酌類解析,其實不是一切的類都須要解析,假如JVM只須要曉得該類能否存在或找出該類的超類,那末就不須要解析。這個辦法是ClassLoader 的進口點。
②defineClass辦法 這個辦法接收類文件的字節數組並把它轉換成Class對象。字節數組可所以從當地文件體系或收集裝入的數據。它把字節碼剖析成運轉時數據構造、校驗有用性等等。
③findSystemClass辦法 findSystemClass辦法從當地文件體系裝入文件。它在當地文件體系中尋覓類文件,假如存在,就應用defineClass將字節數組轉換成 Class對象,以將該文件轉換成類。當運轉Java運用法式時,這是JVM 正常裝入類的缺省機制。
④resolveClass辦法 resolveClass(Class c)辦法解析裝入的類,假如該類曾經被解析過那末將不做處置。當挪用loadClass辦法時,經由過程它的resolve 參數決議能否要停止解析。
⑤findLoadedClass辦法 當挪用loadClass辦法裝入類時,挪用findLoadedClass 辦法來檢查ClassLoader能否已裝入這個類,假如已裝入,那末前往Class對象,不然前往NULL。假如強行裝載已存在的類,將會拋出鏈接毛病。
2.3 裝載的運用
普通來講,我們應用虛擬機的類裝載時須要繼續籠統類java.lang.ClassLoader,個中必需完成的辦法是loadClass(),關於這個辦法須要完成以下操作:(1) 確認類的稱號;(2) 檢討要求要裝載的類能否曾經被裝載;(3) 檢討要求加載的類能否是體系類;(4) 測驗考試從類裝載器的存儲區獲得所要求的類;(5) 在虛擬機中界說所要求的類;(6) 解析所要求的類;(7) 前往所要求的類。
一切的Java 虛擬機都包含一個內置的類裝載器,這個內置的類庫裝載器被稱為根裝載器(bootstrap ClassLoader)。根裝載器的特別的地方是它只可以或許裝載在設計時辰已知的類,是以虛擬機假定由根裝載器所裝載的類都是平安的、可托任的,可以不經由平安認證而直接運轉。當運用法式須要加載其實不是設計時就曉得的類時,必需應用用戶自界說的裝載器(user-defined ClassLoader)。上面我們舉例解釋它的運用。
public abstract class MultiClassLoader extends ClassLoader{ ... public synchronized Class loadClass(String s, boolean flag) throws ClassNotFoundException { /* 檢討類s能否曾經在當地內存*/ Class class1 = (Class)classes.get(s); /* 類s曾經在當地內存*/ if(class1 != null) return class1; try/*用默許的ClassLoader 裝入類*/ { class1 = super.findSystemClass(s); return class1; } catch(ClassNotFoundException _ex) { System.out.println(">> Not a system class."); } /* 獲得類s的字節數組*/ byte abyte0[] = loadClassBytes(s); if(abyte0 == null) throw new ClassNotFoundException(); /* 將類字節數組轉換為類*/ class1 = defineClass(null, abyte0, 0, abyte0.length); if(class1 == null) throw new ClassFormatError(); if(flag) resolveClass(class1); /*解析類*/ /* 將新加載的類放入當地內存*/ classes.put(s, class1); System.out.println(">> Returning newly loaded class."); /* 前往已裝載、解析的類*/ return class1; } ... }
3、Java虛擬機的類裝載道理
後面我們曾經曉得,一個Java運用法式應用兩品種型的類裝載器:根裝載器(bootstrap)和用戶界說的裝載器(user- defined)。根裝載器是Java虛擬機完成的一部門,舉個例子來講,假如一個Java虛擬機是在如今曾經存在而且正在被應用的操作體系的頂部用C法式來完成的,那末根裝載器將是那些C法式的一部門。根裝載器以某種默許的方法將類裝入,包含那些Java API的類。在運轉時代一個Java法式能裝置用戶本身界說的類裝載器。根裝載器是虛擬機固有的一部門,而用戶界說的類裝載器則不是,它是用Java說話寫的,被編譯成class文件以後然後再被裝入到虛擬機,並像其它的任何對象一樣可以被實例化。 Java類裝載器的系統構造以下所示:
圖1 Java的類裝載的系統構造
Java的類裝載模子是一種署理(delegation)模子。當JVM 請求類裝載器CL(ClassLoader)裝載一個類時,CL起首將這個類裝載要求轉發給他的父裝載器。只要當父裝載器沒有裝載並沒有法裝載這個類時,CL才取得裝載這個類的機遇。如許, 一切類裝載器的署理關系組成了一種樹狀的關系。樹的根是類的根裝載器(bootstrap ClassLoader) , 在JVM 中它以"null"表現。除根裝載器之外的類裝載器有且唯一一個父裝載器。在創立一個裝載器時, 假如沒有顯式地給出父裝載器, 那末JVM將默許體系裝載器為其父裝載器。Java的根本類裝載器署理構造如圖2所示:
圖2 Java類裝載的署理構造
上面針對各類類裝載器分離停止具體的解釋。
根(Bootstrap) 裝載器:該裝載器沒有父裝載器,它是JVM完成的一部門,從sun.boot.class.path裝載運轉時庫的焦點代碼。
擴大(Extension) 裝載器:繼續的父裝載器為根裝載器,不像根裝載器能夠與運轉時的操作體系有關,這個類裝載器是用純Java代碼完成的,它從java.ext.dirs (擴大目次)中裝載代碼。
體系(System or Application) 裝載器:裝載器為擴大裝載器,我們都曉得在裝置JDK的時刻要設置情況變量(CLASSPATH ),這個類裝載器就是從java.class.path(CLASSPATH 情況變量)中裝載代碼的,它也是用純Java代碼完成的,同時照樣用戶自界說類裝載器的缺省父裝載器。
小運用法式(Applet) 裝載器: 裝載器為體系裝載器,它從用戶指定的收集上的特定目次裝載小運用法式代碼。
在設計一個類裝載器的時刻,應當知足以下兩個前提:
關於雷同的類名,類裝載器所前往的對象應當是統一個類對象
假如類裝載器CL1將裝載類C的要求轉給類裝載器CL2,那末關於以下的類或接口,CL1和CL2應當前往統一個類對象:a)S為C的直接超類;b)S為C的直接超接口;c)S為C的成員變量的類型;d)S為C的成員辦法或構建器的參數類型;e)S為C的成員辦法的前往類型。
每一個曾經裝載到JVM中的類都隱式含有裝載它的類裝載器的信息。類辦法getClassLoader 可以獲得裝載這個類的類裝載器。一個類裝載器熟悉的類包含它的父裝載器熟悉的類和它本身裝載的類,可見類裝載器熟悉的類是它本身裝載的類的超集。留意我們可以獲得類裝載器的有關的信息,然則曾經裝載到JVM中的類是不克不及更改它的類裝載器的。
Java中的類的裝載進程也就是署理裝載的進程。好比:Web閱讀器中的JVM須要裝載一個小運用法式TestApplet。JVM挪用小運用法式裝載器ACL(Applet ClassLoader)來完成裝載。ACL起首要求它的父裝載器, 即體系裝載器裝載TestApplet能否裝載了這個類, 因為TestApplet不在體系裝載器的裝載途徑中, 所以體系裝載器沒有找到這個類, 也就沒有裝載勝利。接著ACL本身裝載TestApplet。ACL經由過程收集勝利地找到了TestApplet.class 文件並將它導入到了JVM中。在裝載進程中, JVM發明TestAppet是從超類java.applet.Applet繼續的。所以JVM再次挪用ACL來裝載 java.applet.Applet類。ACL又再次按下面的次序裝載Applet類, 成果ACL發明他的父裝載器曾經裝載了這個類, 所以ACL就直接將這個曾經裝載的類前往給了JVM , 完成了Applet類的裝載。接上去,Applet類的超類也一樣處置。最初, TestApplet及一切有關的類都裝載到了JVM中。
4、結論
類的靜態裝載機制是JVM的一項焦點技巧, 也是輕易被疏忽而惹起許多誤會的處所。本文引見了JVM中類裝載的道理、完成和運用,特別剖析了ClassLoader的構造、用處和若何應用自界說的ClassLoader裝載並履行Java類,願望能使讀者對JVM中的類裝載有一個比擬深刻的懂得。