jvm 類載入機制雙親委託機制
1.ClassLoader分為啟動類載入器(BootStrap ClassLoader)、拓展類載入器(Extension ClassLoader)、應用類載入器(App ClassLoader)
1.1 jvm中ClassLoader會協同工作,就是說在載入一個class檔案時,系統會判斷被載入的類是否已經被載入,倘若已經被載入那麼就會返回這個可用的類,要是這個類沒有被載入,就會請求雙親處理,如果雙親請求失敗則會自己載入。我貼出了一段官方原始碼給大家看看
/**
* Loads the class with the specified <a href="#name">binary name</a>. The
* default implementation of this method searches for classes in the
* following order:
*
* <ol>
*
* <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
* has already been loaded. </p></li>
*
* <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
* on the parent class loader. If the parent is <tt>null</tt> the class
* loader built-in to the virtual machine is used, instead. </p></li>
*
* <li><p> Invoke the {@link #findClass(String)} method to find the
* class. </p></li>
*
* </ol>
*
* <p> If the class was found using the above steps, and the
* <tt>resolve</tt> flag is true, this method will then invoke the {@link
* #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
*
* <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
* #findClass(String)}, rather than this method. </p>
*
* <p> Unless overridden, this method synchronizes on the result of
* {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
* during the entire class loading process.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @param resolve
* If <tt>true</tt> then resolve the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);//這裡先判斷此類是否被載入過
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {//當需載入的類沒有被載入過會先請求自己的雙親載入
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {//沒有被載入且請求雙親載入也沒有載入成功,那麼就會自己載入
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
1.2當系統載入一個類時,會先從從頂層的啟動類載入器開始往下載入,一層一層往下找,直到找到相關的類
現在我來驗證一下,首先補充一個知識點
我們會建立好3個java類
TestMain.java、HelloLoader.java、HelloLoader.java(大家肯定會很奇怪為什麼要建立兩個一樣的java類,大家注意往下看其實不一樣方法體不一樣)
HelloLoader.java
package testjvm.classsloader;
public class HelloLoader {
public void print() {
System.out.println("I am in Boot ClassLoader");
}
}
HelloLoader.java
package testjvm.classsloader;
public class HelloLoader {
public void print() {
System.out.println("I am in App ClassLoader");
}
}
package testjvm.classsloader;
public class TestMain {
public static void main(String[] args) {
HelloLoader helloLoader = new HelloLoader();
helloLoader.print();
}
}
首先我們將方法體是App的java類檔案和TestMain 類檔案放到同一包下進行編譯
javac testjvm/classsloader/*.java
將方法體是Boot的java類檔案放到E:\testjava\testjvm\classsloader 目錄進行編譯
javac testjvm/classsloader/*.java
然後到TestMain 包檔案目錄下執行java命令
1.3.判斷一個類是否已經載入是從當前級別類載入器開始判斷的,如果這個類已經在當前級別類載入器中,就不會請求上層類的載入器了,如果不在就會向上層層去判斷,在判斷類是否已經被載入時,頂層類載入器不會詢問底層類載入器
以下方法進行驗證
改寫TestMain.java檔案
package testjvm.classsloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestMain {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
ClassLoader classLoader = TestMain.class.getClassLoader();
byte[] byteLoader = loadClassBytes("testjvm.classsloader.HelloLoader");
Method mdClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
mdClass.setAccessible(true);
mdClass.invoke(classLoader, byteLoader, 0, byteLoader.length);
mdClass.setAccessible(false);
HelloLoader helloLoader = new HelloLoader();
helloLoader.print();
}
private static byte[] loadClassBytes(String name) {
FileInputStream in = null;
try {
String classFilePath = getClassFile(name);
System.out.println("類路徑" + classFilePath);
in = new FileInputStream(classFilePath);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int ch;
while ((ch = in.read()) != -1) {
byte b = (byte) ch;
buffer.write(b);
}
in.close();
return buffer.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static String getClassFile(String name) {
StringBuffer sb = new StringBuffer("E:\\sosino\\workspace\\sosino-shop-630\\testjvm\\src");
name = name.replace('.', File.separatorChar) + ".class";
sb.append(File.separator + name);
return sb.toString();
}
}