1. 程式人生 > >jvm 類載入機制雙親委託機制

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();
    }

}

這裡寫圖片描述