JVM中的類加載器_第1頁
JVM中的類加載器_第2頁
JVM中的類加載器_第3頁
JVM中的類加載器_第4頁
JVM中的類加載器_第5頁
已閱讀5頁,還剩9頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

JVM中的類加載器一個JVM中會有一個自帶的原生類加載器(primordialclassloader),所謂原生的意思確實(shí)是,若是那個jvm是用C寫的,那么那個類加載器也是用C寫好的。另一種確實(shí)是用戶自己寫的classloader。如以下圖所示。JVM中的類加載器所有通過原生加載器加載的類,JVM以為是可信任的;而通過其他加載器加載的,不是可信任的。由于一個jvm往往有多個加載器對象(非原生的),因此要記住某個類A是哪個加載器加載的,如此,那個類A引用到B的時候,JVM還會讓加載A的加載器去加載B。而不是隨機(jī)分派一個。一個加載器類能夠有多個實(shí)例,而一個JVM中有能夠有多個加載器類,因此一個JVM中會有很多個加載器實(shí)例。這些加載器都是用java代碼編寫的,他們都有一個命名空間。另外要注意的是,不同加載器加載的類之間是相互不可見的。除非你明確許諾這么干,那個機(jī)制有平安方面的重要意義。關(guān)鍵字:虛擬機(jī)引導(dǎo)當(dāng)JVM(Java虛擬機(jī))啟動時,會形成由三個類加載器組成的初始類加載器層次結(jié)構(gòu):

bootstrapclassloader

|

extensionclassloader

|

systemclassloader

bootstrapclassloader-引導(dǎo)(也稱為原始)類加載器,它負(fù)責(zé)加載Java的核心類。在Sun的JVM中,在執(zhí)行java的命令中利用-Xbootclasspath選項(xiàng)或利用-D選項(xiàng)指定sun.boot.class.path系統(tǒng)屬性值能夠指定附加的類。那個加載器的是超級特殊的,它事實(shí)上不是java.lang.ClassLoader的子類,而是由JVM自身實(shí)現(xiàn)的。大伙兒能夠通過執(zhí)行以下代碼來取得bootstrapclassloader加載了那些核心類庫:

Java代碼

1.

URL[]

urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();

2.

for

(int

i

=

0;

i

<

urls.length;

i++)

{

3.

System.out.println(urls.toExternalForm());

4.

}

在我的運(yùn)算機(jī)上的結(jié)果為:

file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar

file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar

file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar

file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar

file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar

file:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar

file:/C:/j2sdk1.4.1_01/jre/lib/rt.jar

file:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar

file:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar

file:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar

file:/C:/j2sdk1.4.1_01/jre/lib/jce.jar

file:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar

file:/C:/j2sdk1.4.1_01/jre/classes

這時大伙兒明白了什么緣故咱們不需要在系統(tǒng)屬性CLASSPATH中指定這些類庫了吧,因?yàn)镴VM在啟動的時候就自動加載它們了。

extensionclassloader-擴(kuò)展類加載器,它負(fù)責(zé)加載JRE的擴(kuò)展目錄(JAVA_HOME/jre/lib/ext或由java.ext.dirs系統(tǒng)屬性指定的)中JAR的類包。這為引入除Java核心類之外的新功能提供了一個標(biāo)準(zhǔn)機(jī)制。因?yàn)槟S的擴(kuò)展目錄對所有從同一個JRE中啟動的JVM都是通用的,因此放入那個目錄的JAR類包對所有的JVM和systemclassloader都是可見的。在那個實(shí)例上挪用方式getParent()老是返回空值null,因?yàn)橐龑?dǎo)加載器bootstrapclassloader不是一個真正的ClassLoader實(shí)例。因此當(dāng)大伙兒執(zhí)行以下代碼時:

Java代碼

1.

System.out.println(System.getProperty("java.ext.dirs"));

2.

ClassLoader

extensionClassloader=ClassLoader.getSystemClassLoader().getParent();

3.

System.out.println("the

parent

of

extension

classloader

:

"+extensionClassloader.getParent());

結(jié)果為:

C:\j2sdk1.4.1_01\jre\lib\ext

theparentofextensionclassloader:null

extensionclassloader是systemclassloader的parent,而bootstrapclassloader是extensionclassloader的parent,但它不是一個實(shí)際的classloader,因此為null。

systemclassloader-系統(tǒng)(也稱為應(yīng)用)類加載器,它負(fù)責(zé)在JVM被啟動時,加載來自在命令java中的-classpath或java.class.path系統(tǒng)屬性或CLASSPATH操作系統(tǒng)屬性所指定的JAR類包和類途徑。總能通過靜態(tài)方式ClassLoader.getSystemClassLoader()找到該類加載器。若是沒有專門指定,那么用戶自概念的任何類加載器都將該類加載器作為它的父加載器。執(zhí)行以下代碼即可取得:

System.out.println(System.getProperty("java.class.path"));

輸出結(jié)果那么為用戶在系統(tǒng)屬性里面設(shè)置的CLASSPATH。

classloader加載類用的是通盤負(fù)責(zé)委托機(jī)制。所謂通盤負(fù)責(zé),即是當(dāng)一個classloader加載一個Class的時候,那個Class所依托的和引用的所有Class也由那個classloader負(fù)責(zé)載入,除非是顯式的利用另外一個classloader載入;委托機(jī)制那么是先讓parent(父)類加載器(而不是super,它與parentclassloader類不是繼承關(guān)系)尋覓,只有在parent找不到的時候才從自己的類途徑中去尋覓。另外類加載還采納了cache機(jī)制,也確實(shí)是若是cache中保留了那個Class就直接返回它,若是沒有才從文件中讀取和轉(zhuǎn)換成Class,并存入cache,這確實(shí)是什么緣故咱們修改了Class可是必需從頭啟動JVM才能生效的緣故。

每一個ClassLoader加載Class的進(jìn)程是:

1.檢測此Class是不是載入過(即在cache中是不是有此Class),若是有到8,若是沒有到2

2.若是parentclassloader不存在(沒有parent,那parent必然是bootstrapclassloader了),到4

3.請求parentclassloader載入,若是成功到8,不成功到5

4.請求jvm從bootstrapclassloader中載入,若是成功到8

5.尋覓Class文件(從與此classloader相關(guān)的類途徑中尋覓)。若是找不到那么到7.

6.從文件中載入Class,到8.

7.拋出ClassNotFoundException.

8.返回Class.

其中5.6步咱們能夠通過覆蓋ClassLoader的findClass方式來實(shí)現(xiàn)自己的載入策略。乃至覆蓋loadClass方式來實(shí)現(xiàn)自己的載入進(jìn)程。

類加載器的順序是:

先是bootstrapclassloader,然后是extensionclassloader,最后才是systemclassloader。大伙兒會發(fā)覺加載的Class越是重要的越在靠前面。如此做的緣故是出于平安性的考慮,試想若是systemclassloader“親自”加載了一個具有破壞性的“java.lang.System”類的后果吧。這種委托機(jī)制保證了用戶即便具有一個如此的類,也把它加入到了類途徑中,可是它永久可不能被載入,因?yàn)槟莻€類老是由bootstrapclassloader來加載的。大伙兒能夠執(zhí)行一下以下的代碼:

System.out.println(System.class.getClassLoader());

將會看到結(jié)果是null,這就說明java.lang.System是由bootstrapclassloader加載的,因?yàn)閎ootstrapclassloader不是一個真正的ClassLoader實(shí)例,而是由JVM實(shí)現(xiàn)的,正如前面已經(jīng)說過的。

下面就讓咱們來看看JVM是如何來為咱們來成立類加載器的結(jié)構(gòu)的:

sun.misc.Launcher,顧名思義,當(dāng)你執(zhí)行java命令的時候,JVM會先利用bootstrapclassloader載入并初始化一個Launcher,執(zhí)行下來代碼:

Java代碼

1.

System.out.println("the

Launcher's

classloader

is

"+sun.misc.Launcher.getLauncher().getClass().getClassLoader());

2.

theLauncher'sclassloaderisnull(因?yàn)槭怯胋ootstrapclassloader加載,因此classloader為null)

Launcher會依照系統(tǒng)和命令設(shè)定初始化好classloader結(jié)構(gòu),JVM就用它來取得extensionclassloader和systemclassloader,并載入所有的需要載入的Class,最后執(zhí)行java命令指定的帶有靜態(tài)的main方式的Class。extensionclassloader事實(shí)上是sun.misc.Launcher$ExtClassLoader類的一個實(shí)例,systemclassloader事實(shí)上是sun.misc.Launcher$AppClassLoader類的一個實(shí)例。而且都是.URLClassLoader的子類。

讓咱們來看看Launcher初試化的進(jìn)程的部份代碼。

Launcher的部份代碼:

Java代碼

1.

public

class

Launcher

{

2.

public

Launcher()

{

3.

ExtClassLoader

extclassloader;

4.

try

{

5.

//初始化extension

classloader

6.

extclassloader

=

ExtClassLoader.getExtClassLoader();

7.

}

catch(IOException

ioexception)

{

8.

throw

new

InternalError("Could

not

create

extension

class

loader");

9.

}

10.

try

{

11.

//初始化system

classloader,parent是extension

classloader

12.

loader

=

AppClassLoader.getAppClassLoader(extclassloader);

13.

}

catch(IOException

ioexception1)

{

14.

throw

new

InternalError("Could

not

create

application

class

loader");

15.

}

16.

//將system

classloader設(shè)置成當(dāng)前線程的context

classloader(將在后面加以介紹)

17.

Thread.currentThread().setContextClassLoader(loader);

18.

......

19.

}

20.

public

ClassLoader

getClassLoader()

{

21.

//返回system

classloader

22.

return

loader;

23.

}

24.

}

extensionclassloader的部份代碼:

Java代碼

1.

static

class

Launcher$ExtClassLoader

extends

URLClassLoader

{

2.

3.

public

static

Launcher$ExtClassLoader

getExtClassLoader()

4.

throws

IOException

5.

{

6.

File

afile[]

=

getExtDirs();

7.

return

(Launcher$ExtClassLoader)AccessController.doPrivileged(new

Launcher$1(afile));

8.

}

9.

private

static

File[]

getExtDirs()

{

10.

//取得系統(tǒng)屬性“java.ext.dirs”

11.

String

s

=

System.getProperty("java.ext.dirs");

12.

File

afile[];

13.

if(s

!=

null)

{

14.

StringTokenizer

stringtokenizer

=

new

StringTokenizer(s,

File.pathSeparator);

15.

int

i

=

stringtokenizer.countTokens();

16.

afile

=

new

File;

17.

for(int

j

=

0;

j

<

i;

j++)

18.

afile[j]

=

new

File(stringtokenizer.nextToken());

19.

20.

}

else

{

21.

afile

=

new

File[0];

22.

}

23.

return

afile;

24.

}

25.

}

systemclassloader的部份代碼:

Java代碼

1.

static

class

Launcher$AppClassLoader

extends

URLClassLoader

2.

{

3.

4.

public

static

ClassLoader

getAppClassLoader(ClassLoader

classloader)

5.

throws

IOException

6.

{

7.

//取得系統(tǒng)屬性“java.class.path”

8.

String

s

=

System.getProperty("java.class.path");

9.

File

afile[]

=

s

!=

null

?

Launcher.access$200(s)

:

new

File[0];

10.

return

(Launcher$AppClassLoader)AccessController.doPrivileged(new

Launcher$2(s,

afile,

classloader));

11.

}

12.

}

看了源代碼大伙兒就清楚了吧,extensionclassloader是利用系統(tǒng)屬性“java.ext.dirs”設(shè)置類搜索途徑的,而且沒有parent。systemclassloader是利用系統(tǒng)屬性“java.class.path”設(shè)置類搜索途徑的,而且有一個parentclassloader。Launcher初始化extensionclassloader,systemclassloader,并將systemclassloader設(shè)置成為contextclassloader,可是僅僅返回systemclassloader給JVM。

那個地址怎么又出來一個contextclassloader呢?它有什么用呢?咱們在成立一個線程Thread的時候,能夠?yàn)槟莻€線程通過setContextClassLoader方式來指定一個適合的classloader作為那個線程的contextclassloader,當(dāng)此線程運(yùn)行的時候,咱們能夠通過getContextClassLoader方式來取得此contextclassloader,就能夠夠用它來載入咱們所需要的Class。默許的是systemclassloader。利用那個特性,咱們能夠“打破”classloader委托機(jī)制了,父classloader能夠獲適當(dāng)前線程的contextclassloader,而那個contextclassloader能夠是它的子classloader或其他的classloader,那么父classloader就能夠夠從其取得所需的Class,這就打破了只能向父classloader請求的限制了。那個機(jī)制能夠知足當(dāng)咱們的classpath是在運(yùn)行時才確信,并由定制的classloader加載的時候,由systemclassloader(即在jvmclasspath中)加載的class能夠通過contextclassloader取得定制的classloader并加載入特定的class(一般是抽象類和接口,定制的classloader中是其實(shí)現(xiàn)),例如web應(yīng)用中的servlet確實(shí)是用這種機(jī)制加載的.

好了,此刻咱們了解了classloader的結(jié)構(gòu)和工作原理,那么咱們?nèi)绾螌?shí)此刻運(yùn)行時的動態(tài)載入和更新呢?只要咱們能夠動態(tài)改變類搜索途徑和清除classloader的cache中已經(jīng)載入的Class就好了,有兩個方案,一是咱們繼承一個classloader,覆蓋loadclass方式,動態(tài)的尋覓Class文件并利用defineClass方式來;另一個那么超級簡單有效,只要從頭利用一個新的類搜索途徑來new一個classloader就好了,如此即更新了類搜索途徑以便來載入新的Class,也從頭生成了一個空白的cache(固然,類搜索途徑不必然必需更改)。噢,太好了,咱們幾乎不用做什么工作,URLClassLoader正是一個符合咱們要求的classloader!咱們能夠直接利用或繼承它就能夠夠了!

這是j2se1.4API的doc中URLClassLoader的兩個構(gòu)造器的描述:

URLClassLoader(URL[]urls)

ConstructsanewURLClassLoaderforthespecifiedURLsusingthedefaultdelegationparentClassLoader.

URLClassLoader(URL[]urls,ClassLoaderparent)

ConstructsanewURLClassLoaderforthegivenURLs.

其中URL[]urls確實(shí)是咱們要設(shè)置的類搜索途徑,parent確實(shí)是那個classloader的parentclassloader,默許的是systemclassloader。

好,此刻咱們能夠動態(tài)的載入Class了,如此咱們就能夠夠利用newInstance方式來取得一個Object。但咱們?nèi)绾螌⒋薕bject造型呢?能夠?qū)⒋薕bject造型成它本身的Class嗎?

第一讓咱們來分析一下java源文件的編譯,運(yùn)行吧!javac命令是挪用“JAVA_HOME/lib/tools.jar”中的“com.sun.tools.javac.Main”的compile方式來編譯:

Java代碼

1.

public

static

int

compile(String

as[]);

2.

3.

public

static

int

compile(String

as[],

PrintWriter

printwriter);

返回0表示編譯成功,字符串?dāng)?shù)組as那么是咱們用javac命令編譯時的參數(shù),以空格劃分。例如:

javac-classpathc:\foo\bar.jar;.-dc:\c:\Some.java

那么字符串?dāng)?shù)組as為{"-classpath","c:\\foo\\bar.jar;.","-d","c:\\","c:\\Some.java"},若是帶有PrintWriter參數(shù),那么會把編譯信息出到那個指定的printWriter中。默許的輸出是System.err。

其中Main是由JVM利用Launcher初始化的systemclassloader載入的,依照通盤負(fù)責(zé)原那么,編譯器在解析那個java源文件時所發(fā)覺的它所依托和引用的所有Class也將由systemclassloader載入,若是systemclassloader不能載入某個Class時,編譯器將拋出一個“cannotresolvesymbol”錯誤。

因此第一編譯就通只是,也確實(shí)是編譯器無法編譯一個引用了不在CLASSPATH中的未知Class的java源文件,而由于拼寫錯誤或沒有把所需類庫放到CLASSPATH中,大伙兒必然常常看到那個“cannotresolvesymbol”那個編譯錯誤吧!

第二,確實(shí)是咱們把那個Class放到編譯途徑中,成功的進(jìn)行了編譯,然后在運(yùn)行的時候不把它放入到CLASSPATH中而利用咱們自己的classloader來動態(tài)載入那個Class,這時也會顯現(xiàn)“java.lang.NoClassDefFoundError”的違例,什么緣故呢?

咱們再來分析一下,第一挪用那個造型語句的可執(zhí)行的Class必然是由JVM利用Launcher初始化的systemclassloader載入的,依照通盤負(fù)責(zé)原那么,當(dāng)咱們進(jìn)行造型的時候,JVM也會利用systemclassloader來嘗試載入那個Class來對實(shí)例進(jìn)行造型,自然在systemclassloader尋覓不到那個Class時就會拋出“java.lang.NoClassDefFoundError”的違例。

OK,此刻讓咱們來總結(jié)一下,java文件的編譯和Class的載入執(zhí)行,都是利用Launcher初始化的systemclassloader作為類載入器的,咱們無法動態(tài)的改變systemclassloader,更無法讓JVM利用咱們自己的classloader來替換systemclassloader,依照通盤負(fù)責(zé)原那么,就限制了編譯和運(yùn)行時,咱們無法直接顯式的利用一個systemclassloader尋覓不到的Class,即咱們只能利用Java核心類庫,擴(kuò)展類庫和CLASSPATH中的類庫中的Class。

還不死心!再嘗試一下這種情形,咱們把那個Class也放入到CLASSPATH中,讓systemclassloader能夠識別和載入。然后咱們通過自己的classloader來從指定的class文件中載入那個Class(不能夠委托parent載入,因?yàn)槿绱藭籹ystemclassloader從CLASSPATH中將其載入),然后實(shí)例化一個Object,并造型成那個Class,如此JVM也識別那個Class(因?yàn)閟ystemclassloader能夠定位和載入那個Class從CLASSPATH中),載入的也不是CLASSPATH中的那個Class,而是從CLASSPATH外動態(tài)載入的,如此總行了吧!十分不幸的是,這時會顯現(xiàn)“java.lang.ClassCastException”違例。

什么緣故呢?咱們也來分析一下,不錯,咱們盡管從CLASSPATH外利用咱們自己的classloader動態(tài)載入了那個Class,但將它的實(shí)例造型的時候是JVM會利用systemclassloader來再次載入那個Class,并嘗試將利用咱們的自己的classloader載入的Class的一個實(shí)例造型為systemclassloader載入的那個Class(另外的一個)。大伙兒發(fā)覺什么問題了嗎?也確實(shí)是咱們嘗試將從一個classloader載入的Class的一個實(shí)例造型為另外一個classloader載入的Class,盡管這兩個Class的名字一樣,乃至是從同一個class文件中載入。但不幸的是JVM卻以為那個兩個Class是不同的,即JVM以為不同的classloader載入的相同的名字的Class(即便是從同一個class文件中載入的)是不同的!如此做的緣故我想可能也是要緊出于平安性考慮,如此就保證所有的核心Java類都是systemclassloader載入的,咱們無法用自己的classloader載入的相同名字的Class的實(shí)例來替換它們的實(shí)例。

看到那個地址,伶俐的讀者必然想到了該如何動態(tài)載入咱們的Class,實(shí)例化,造型并挪用了吧!

那確實(shí)是利用面向?qū)ο蟮拇篌w特性之一的多形性。咱們把咱們動態(tài)載入的Class的實(shí)例造型成它的一個systemclassloader所能識別的父類就好了!這是什么緣故呢?咱們?nèi)允且賮矸治鲆淮巍.?dāng)咱們用咱們自己的classloader來動態(tài)載入這咱們只要把那個Class的時候,發(fā)覺它有一個父類Class,在載入它之前JVM先會載入那個父類Class,那個父類Class是systemclassloader所能識別的,依照委托機(jī)制,它將由systemclassloader載入,然后咱們的classloader再載入那個Class,創(chuàng)建一個實(shí)例,造型為那個父類Class,注意了,造型成那個父類Class的時候(也確實(shí)是上溯)是面向?qū)ο蟮膉ava語言所許諾的而且JVM也支持的,JVM就利用systemclassloader再次載入那個父類Class,然后將此實(shí)例造型為那個父類Class。大伙兒能夠從那個進(jìn)程發(fā)覺那個父類Class都是由systemclassloader載入的,也確實(shí)是同一個classloader載入的同一個Class,因此造型的時候可不能顯現(xiàn)任何異樣。而依照多形性,挪用那個父類的方式時,真正執(zhí)行的是那個Class(非父類Class)的覆蓋了父類方式的方式。這些方式中也能夠引用systemclassloader不能識別的Class,因?yàn)橐勒胀ūP負(fù)責(zé)原那么,只要載入那個Class的classloader即咱們自己概念的classloader能夠定位和載入這些Class就好了。

如此咱們就能夠夠事前概念好一組接口或基類并放入CLASSPATH中,然后在執(zhí)行的時

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論