早期的java程序員可能只要懂基本語(yǔ)法,還有少數(shù)的項(xiàng)目經(jīng)驗(yàn)就可以找到一份比較好的工作。Java和java社區(qū)的發(fā)展,更多的人了解它,深入它。現(xiàn)在java程序員了解一些語(yǔ)法我看還遠(yuǎn)遠(yuǎn)不夠了,對(duì)于jvm的了解和深入是非常重要的。網(wǎng)民的增多,網(wǎng)站的剛性需求,很多網(wǎng)站面臨高性能,高并發(fā)等等一系列的問題。沒有深入jvm的java程序員是很難寫出高質(zhì)量高并發(fā)的代碼(也許一棒子打死所有人了,但我想絕大部分是肯定的)。
Osgi也許你并不陌生,但是他底層的實(shí)現(xiàn)機(jī)制你可能沒去了解過。如果你是個(gè)打破砂鍋問到底的人,你肯定會(huì)想知道osgi是如何做到的。但是你沒有了解jvm的類加載體系,你肯定很難理解osgi是如果做到類隔離等一系列的問題。不過想完整理解osgi還需要其他很多方面的知識(shí),但是它基本的機(jī)制還是的了解jvm的類加載機(jī)制。
Java類庫(kù)有些包只是定義了一個(gè)標(biāo)準(zhǔn),具體的實(shí)現(xiàn)都是由具體的供應(yīng)商來提供。Java與數(shù)據(jù)庫(kù)連接就是一個(gè)很好的例子。Java.sql類庫(kù)只是定義了java與數(shù)據(jù)庫(kù)連接的標(biāo)準(zhǔn),那么與mysql就需要msyql的驅(qū)動(dòng),oracle就需要oracle的驅(qū)動(dòng),而java.sql類庫(kù)是由bootstrap classloader加載,驅(qū)動(dòng)包中的類是由system classloader來加載,不同類加載器加載的類是無法相互認(rèn)識(shí),所以自然也無法正常提供功能了。jvm又是提供了什么機(jī)制讓他們交互呢?如果你確實(shí)對(duì)這些問題毫無頭緒的話,那么我覺得你真的要好好理解下jvm類加載體系。
這篇文章主要是介紹下jvm類加載的機(jī)制基礎(chǔ)知識(shí)。關(guān)于其他相關(guān)涉及,有時(shí)間的話,我會(huì)單獨(dú)寫文章來介紹。
1、 java類加載器

1.1 Bootstrap classloader:sun jdk是用c++實(shí)現(xiàn),所以在代碼中你是無法獲取此加載器的實(shí)例。此加載器主要負(fù)責(zé)加載$JAVA_HOME/jre/lib/rt.jar。java類中獲取結(jié)果為null,這里可以用一個(gè)例子跑下證明:
public class Test {
public static void main(String[] arg) throws Exception{
ClassLoader classloader = Test.class.getClassLoader();
System.out.println(classloader);
System.out.println(classloader.getParent());
System.out.println(classloader.getParent().getParent());
}
}
輸出結(jié)果:
sun.misc.Launcher$AppClassLoader@19821f
sun.misc.Launcher$ExtClassLoader@addbf1
null
最后輸出的null就是代表bootstrap classloader。
1.2 Extension classloader:主要加載擴(kuò)展功能的jar,$JAVA_HOME/jre/lib/ext/*.jar。
1.3 System classloader:加載claspath中的jar包。
1.4自定義 classloader:主要加載你指定的目錄中的包和類。
2、 雙親委派模型
系統(tǒng)運(yùn)行時(shí),我們請(qǐng)求加載String類,此時(shí)System Classloader自己不找classpath中的包,把請(qǐng)求轉(zhuǎn)發(fā)給Extension Classloader,但它也不做查找,又轉(zhuǎn)發(fā)給Bootstrap Classloader,但是它發(fā)現(xiàn)自己沒有parent了。于是他在rt.jar包中找到String類并加載到j(luò)vm中提供使用。Jvm為什么要這么實(shí)現(xiàn)呢?其實(shí)和java安全體系有關(guān)。假設(shè)jvm不是這么實(shí)現(xiàn),我們自定義一個(gè)String類,做一些破壞,那么運(yùn)行jvm的機(jī)器肯定要受到損壞。具體例子:
public class String {
public static void main(String[] args) {
System.out.println("hello world");
}
}
我們運(yùn)行自定義String類的時(shí)候報(bào)錯(cuò)了,說沒有main方法,可我們定義的明明有的,嘿嘿,委派機(jī)制的緣故最后加載到的是由bootstrap classloader在rt.jar包中的String,那個(gè)類是沒有main方法,因此報(bào)錯(cuò)了。

3、 類隔離
jvm中的類是:類加載器+包名+類名。比如:URLClassLoader1,URLClassLoader2分別加載com.test.Test的時(shí)候會(huì)加載兩次,因?yàn)槊總€(gè)classloader中的類對(duì)于其他classloader來說是隔離的,不認(rèn)識(shí)的。例子:
import java.net.URL;
import java.net.URLClassLoader;
public class CustomClassloaderTest {
public static void main(String[] args) throws Exception {
URL url = new URL("file:/g:/");
URLClassLoader ucl = new URLClassLoader(new URL[]{url});
Class c = ucl.loadClass("Yang");
c.newInstance();
System.out.println(c.getClassLoader());
URLClassLoader ucl2 = new URLClassLoader(new URL[]{url});
Class c2 = ucl2.loadClass("Yang");
c2.newInstance();
System.out.println(c2.getClassLoader());
}
}
大家把Yang類存在g盤下。
public class Yang {
static {
System.out.println("Yang");
}
}
運(yùn)行結(jié)果:
Yang
java.net.URLClassLoader@c17164
Yang
java.net.URLClassLoader@61de33
看到每次加載Yang類的時(shí)候都輸出Yang,說明Yang類被加載了兩次。
如果你不確信,可以修改下代碼,讓同一classloader加載Yang類兩次
import java.net.URL;
import java.net.URLClassLoader;
public class CustomClassloaderTest {
public static void main(String[] args) throws Exception {
URL url = new URL("file:/g:/");
URLClassLoader ucl = new URLClassLoader(new URL[]{url});
Class c = ucl.loadClass("Yang");
c.newInstance();
System.out.println(c.getClassLoader());
Class c2 = ucl.loadClass("Yang");
c2.newInstance();
System.out.println(c2.getClassLoader());
}
}
看看輸出結(jié)果:
Yang
java.net.URLClassLoader@c17164
java.net.URLClassLoader@c17164
結(jié)果中只輸出了一次Yang。因此可以證明我們最開始說的類隔離。
4、 線程上下文類加載器
我們理解了雙親委派模型,那么目前只有由下向上單向?qū)ふ翌悾╯ystem->extension->bootstrap)
。我們?cè)谧铋_始的時(shí)候說過,java.sql包中的類由bootstrap或extension classloader加載,而mysql驅(qū)動(dòng)包是在classpath中由system來加載,但bootstrap中的類是無法找到system classloader中的類,此時(shí)靠線程上下文類加載器來解決。線程上下文類加載器主要就是能讓jvm類加載模型具有了向下尋找的可能,bootstrap->extension->system,如果不做任何設(shè)置,線程上下文類加載器默認(rèn)是system classloader。本來這里想寫一個(gè)例子的,可是有點(diǎn)麻煩,所以下次單獨(dú)寫一篇關(guān)于這方面的知識(shí)。