ClassLoader
將Class加載到內(nèi)存
結(jié)構(gòu)
- BootstrapClassLoader:加載Java核心庫(JAVA_HOME/jre/lib),唯一一個(gè)使用本地代碼編寫的加載器
- ExtensionClassLoader:加載擴(kuò)展庫(JAVA_HOME/jre/lib/ext和系統(tǒng)參數(shù)java.ext.dirs指定的目錄)卤档,它的父加載器是null(因?yàn)锽ootstrapClassLoader是使用C++實(shí)現(xiàn)的,沒有對(duì)應(yīng)的java類)
- SystemClassLoader:加載應(yīng)用類的類文件(Classpath下的類文件)
- UserDefineClassLoader:加載用戶定義的類文件抛虏,可以從網(wǎng)絡(luò)或者數(shù)據(jù)庫中加載類文件阳谍,實(shí)現(xiàn)類文件加密解密,動(dòng)態(tài)地創(chuàng)建符合應(yīng)用特殊需要的定制化類等
雙親委派機(jī)制
加載器在接收到加載類的請求時(shí)郭卫,首先檢查自己的緩存战授,確認(rèn)類是否已被加載页藻,如果沒有加載,則將請求委托給父加載器植兰,依次遞歸份帐,如果父加載器完成加載,則成功返回楣导,否則才自己去加載
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
// 檢查類是否已被加載
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
// 委托父類去加載
c = parent.loadClass(name, false);
} else {
// parent為空废境,代表父類是BootstrapClassLoader
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// 父類沒有加載,才自己去加載
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
意義:避免重復(fù)加載爷辙,避免安全因素(如果不采用這種機(jī)制彬坏,那么系統(tǒng)核心的類就可以被隨意替換)
** 不同類加載器加載的類不是相同類型:在Java中朦促,一個(gè)類的全名(包名+類名)作為其標(biāo)識(shí)膝晾,但在JVM中,一個(gè)類用其全名+類加載器作為唯一標(biāo)識(shí)务冕,不同類加載器加載的類置于不同的命名空間中**
線程上下文類加載器
雙親委派模式不能解決全部的加載問題血当。
Java提供了很多服務(wù)提供者接口(Service Provider Interface,SPI)禀忆,常見的有JDBC臊旭、JNDI、JAXP等箩退。這些SPI接口由Java核心庫提供(通過BootstrapClassLoader加載)离熏,而它們的實(shí)現(xiàn)由第三方庫提供(通過SystemClassLoader加載)。但很多時(shí)候戴涝,SPI接口需要加載具體的實(shí)現(xiàn)類滋戳,如 JAXP 中的javax.xml.parsers.DocumentBuilderFactory類中的newInstance()方法用來生成一個(gè)新的DocumentBuilderFactory的實(shí)例钻蔑。這里的實(shí)例的真正的類是繼承自 javax.xml.parsers.DocumentBuilderFactory,由 SPI 的實(shí)現(xiàn)所提供的奸鸯。如在 Apache Xerces 中咪笑,實(shí)現(xiàn)的類是 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl。問題是BootstrapClassLoader是無法找到 SPI 的實(shí)現(xiàn)類的娄涩,因?yàn)樗患虞d Java 的核心庫窗怒。它也不能代理給SystemClassLoader,因?yàn)樗荢ystemClassLoader的祖先類蓄拣。
而線程上下文類加載器就是解決這個(gè)問題扬虚,如果不做任何的設(shè)置,Java的線程的上下文類加載器默認(rèn)就是SystemClassLoader球恤。在SPI接口的代碼中使用線程上下文類加載器孔轴,就可以成功的加載到SPI實(shí)現(xiàn)的類。
使用線程上下文加載器碎捺,要注意保證多個(gè)需要通信的線程間的類加載器應(yīng)該是同一個(gè)路鹰,防止因?yàn)椴煌念惣虞d器導(dǎo)致類型轉(zhuǎn)換異常(ClassCastException)。
運(yùn)行時(shí)區(qū)域
Method Area
已加載的類信息(類結(jié)構(gòu)收厨、方法晋柱、字段、靜態(tài)變量)
常量池:字符串诵叁、整形(-127-128)
一般稱為Permanent Generation
線程執(zhí)行
每個(gè)線程都有一個(gè)PC Register雁竞、JVM Stack、Native Method Stack
PC Register:下一條要執(zhí)行的指令的地址
JVM Stack:包含一系列的Stack Frame拧额,每次方法調(diào)用都會(huì)創(chuàng)建一個(gè)Frame并壓棧碑诉,每個(gè)棧幀都對(duì)應(yīng)一個(gè)被調(diào)用的方法(使用遞歸容易讓棧溢出,通過-Xss設(shè)置棧大薪慕酢)进栽。同時(shí)那些方法內(nèi)的局部變量,也是在這里創(chuàng)建
Heap
存放實(shí)例對(duì)象和數(shù)組
分成Young恭垦、Tenured快毛、Permanent三個(gè)不同區(qū)域,其中Young又分成Eden和兩個(gè)相同大小的Survivor:From番挺、To
為什么要分代:不同對(duì)象的生命周期是不一樣的唠帝,采用不同的收集算法,可以提高回收效率
例子
public class Test {
public static void main(String[] args) {
public Test2 t2 = new Test2();
//JVM將Test2類信息加載到方法區(qū),new Test2()實(shí)例保存在堆區(qū),Test2引用保存在棧區(qū)
}
}
參考
http://docs.oracle.com/javase/specs/jvms/se7/jvms7.pdf
http://blog.csdn.net/zhoudaxia/article/details/35897057
http://www.ibm.com/developerworks/cn/java/j-lo-classloader/