【JVM】之類加載機制

1、類加載簡介java

        類加載機制:JVM把class文件加載到內存,並對數據進行校驗、解析和初始化,最終造成JVM能夠直接使用的Java類型的過程。apache

2、bootstrap

        加載:將class文件字節碼內容經過類加載器加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲方法區類數據的訪問入口(外部能夠經過Class對象,做爲操做類的入口)。這個過程須要類加載器參與。數組

        連接:將Java類的二進制代碼合併到JVM的運行狀態之中的過程。【驗證:確保加載的類信息符合JVM規範,沒有安全方面的問題。準備:正式爲類變量(static變量)分配內存並設置類變量初始值的階段,這些內存都將在方法區中進行分配。解析虛擬機常量池內的符號引用替換爲直接引用的過程。】緩存

        初始化:1.初始化階段是執行類構造器<clinit>()方法的過程。類構造器<clinit>()方法是由編譯器自動收集類中的全部類變量的賦值動做靜態語句塊(static塊)中的語句合併產生的。 2.當初始化一個類的時候,若是發現其父類尚未進行過初始化,則須要先觸發其父類的初始化。 3.虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步。當訪問一個Java類的靜態域時,只有真正聲明這個域的類纔會被初始化。tomcat

        類的主動引用(必定會發生類的初始化)安全

        - new 一個類的對象服務器

        - 調用類的靜態成員(除了final常量)和靜態方法網絡

        - 使用java.lang.reflect包的方法對類進行反射調用數據結構

        - 當虛擬機啓動,java Hello,則必定會初始化Hello類,說白了就是先啓動main方法所在的類

        - 當初始化一個類,若是其父類沒有被初始化,則先會初始化他的父類

        類的被動引用(不會發生類的初始化)

        - 當訪問一個靜態域時,只有真正聲明這個域的類纔會被初始化

            . 經過子類引用父類的靜態變量,不會致使子類初始化

        - 經過數組定義類引用,不會觸發此類的初始化

        - 引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)

3、

public class Demo01 {
    public static void main(String[] args) {
        System.out.println("初始化Demo01");
        //A a = new A();
       // System.out.println("width= " + A.width);
       // System.out.println(A.MAX);
       // System.out.println(Child_A.width);
        A[] arr = new A[]{};
    }
}
class A extends Father_A {
    static {
        System.out.println("靜態初始化塊");
        width = 200;
    }
    public static int width = 100;
    public static final int MAX = 100;
    public A() {
        System.out.println("構造函數初始化中");
    }
    {
        System.out.println("初始化塊");
        width = 300;
    }
}
class Father_A {
    static {
        System.out.println("靜態初始化塊——Father_A");
    }
}

class Child_A extends A {
    static {
        System.out.println("靜態初始化Child_A");
    }
}

4、深刻類加載器

        類加載器的做用:將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲方法區類數據的訪問入口。

        類緩存:標準的JavaSE類加載器能夠按要求查找類,但一旦某個類被加載到類加載器中,它將維持加載(緩存)一段時間。不過,JVM垃圾收集器能夠回收這些Class對象。

    類加載器的層次結構(樹狀結構)

        引導類加載器(bootstrap class loader)

        - 它用來加載Java的核心庫(JAVA_HOME/jre/lib/rt.jar,或sum.boot.class.path路徑下的內容),是用原生代碼來實現的,並不繼承自java.lang.ClassLoader。

        - 加載擴展類和應用程序類加載器,並指定他們的父類加載器。

        擴展類加載器(extensions class loader)

        - 用來加載Java的擴展庫(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路徑下的內容)。Java虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄路面查找並加載Java類。

        - 由sum.misc.Launcher$ExtClassLoader實現

        應用程序類加載器(application class loader)

        - 它根據Java應用的類路徑(classpath,java.class.path路徑)。通常來講,Java應用的類都是由它來完成加載的。

        - 由sun.misc.Launcher$AppClassLoader實現。

        自定義類加載器

            自定義類加載器的流程

            - 繼承:java.lang.ClassLoader

            - 首先檢查請求的類型是否已經被這個類裝載器裝載到命名空間中了,若是已經裝載,直接返回;

            - 委派類加載請求給父類加載器,若是父類加載器可以完成,則返回父類加載器加載的Class實例;

            - 調用本類加載器的findClass(...)方法,試圖獲取對應的字節碼,若是獲取的到,則調用defineClass(...)導入類型到方法區;若是獲取不到對應的字節碼或者其它緣由失敗,返回異常給loadClass(...),loadClass(...)轉拋異常,終止加載過程;

            - 注意:被兩個類加載器加載的同一個類,JVM不認爲是相同的類。

        文件類加載器

import org.apache.commons.io.IOUtils;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileSystemClassLoader extends ClassLoader {

    private String rootDir;

    public FileSystemClassLoader(String rootDir) {
        this.rootDir = rootDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(name);
        /**
         * 先查詢有沒有加載過這個類,若是已經加載,則直接返回加載好的類;若是沒有,則加載新的類
         */
        if (clazz != null) {
            return clazz;
        } else {
            /**
             * 委派類加載請求給父類加載器
             */
            ClassLoader parent = this.getParent();
            try {
                clazz = parent.loadClass(name);
            } catch (Exception e) {

            }
            if (clazz != null) {
                return clazz;
            } else {
                byte[] classData = getClassData(name);
                if (classData == null) {
                    throw new ClassNotFoundException();
                } else {
                    clazz = defineClass(name, classData,0, classData.length);
                }
            }
        }
        return clazz;
    }

    private byte[] getClassData(String classname) {
        String path = rootDir + "/" + classname.replace(".", "/") + ".class";
        /**
         * 注:IOUtils,可使用它將流中的數組轉換成字節數組
         */
        InputStream is = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            is = new FileInputStream(path);
            byte[] buffer = new byte[1024];
            int temp = 0;
            while ((temp = is.read(buffer)) != -1) {
                baos.write(buffer, 0, temp);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (baos != null) {
                    baos.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
      /*  InputStream is = null;
        try {
            is = new FileInputStream(path);
            byte[] bytes = IOUtils.toByteArray(is);
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }*/
    }
}
ipublic class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        FileSystemClassLoader classLoader = new FileSystemClassLoader("/Users/Work/mimidai/mimidai-test");
        FileSystemClassLoader classLoader2 = new FileSystemClassLoader("/Users/Work/mimidai/mimidai-test");

        Class<?> clazz = classLoader.loadClass("HelloWordI");
        Class<?> clazz1 = classLoader.loadClass("HelloWordI");
        Class<?> clazz2 = classLoader2.loadClass("HelloWordI");
        Class<?> clazz3 = classLoader2.loadClass("java.lang.String");
        Class<?> clazz4 = classLoader2.loadClass("com.mimidai.test2.Demo01");

        System.out.println(clazz.hashCode());
        System.out.println(clazz1.hashCode());
        System.out.println(clazz2.hashCode());
        System.out.println(clazz.getClassLoader());
        System.out.println(clazz2.getClassLoader());
        System.out.println(clazz3.getClassLoader());
        System.out.println(clazz4.getClassLoader());
    }
}

輸出結果

363771819
363771819
2065951873
com.mimidai.test2.FileSystemClassLoader@16b98e56
com.mimidai.test2.FileSystemClassLoader@27d6c5e0
null
sun.misc.Launcher$AppClassLoader@18b4aac2

        網絡類加載器

import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.net.URL;

public class NetClassLoader extends ClassLoader {

    private String rootURL;

    public NetClassLoader(String rootURL) {
        this.rootURL = rootURL;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class<?> clazz = this.findLoadedClass(name);

        if (clazz != null) {
            return clazz;
        } else {
            ClassLoader parentClassLoader = this.getParent();
            try {
                clazz = parentClassLoader.loadClass(name);
            } catch (Exception e) {

            }
            if (clazz != null) {
                return clazz;
            } else {
                byte[] classData = getClassData(name);
                if (classData != null) {
                    clazz = defineClass(name, classData,0, classData.length);
                    return clazz;
                } else {
                    throw new ClassNotFoundException();
                }
            }
        }
    }

    private byte[] getClassData(String name) {
        String path = rootURL + name.replace(".", "/");

        InputStream is = null;
        try {
            URL url = new URL(path);
            is = url.openStream();
            byte[] clazzDate = IOUtils.toByteArray(is);
            return clazzDate;
        } catch (Exception e) {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (Exception e1) {

            }
            return null;
        }
    }
}

        加密解密類加載器(取反操做,DES對稱加密解密)

5、java.lang.ClassLoader類介紹

    - java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱,找到或者生成器對應的字節代碼,而後從這些字節代碼中定義出一個Java類,即java.lang.Class類的一個實例。

    - 除此以外,ClassLoader還負責加載Java應用所需的資源,如圖像文件和配置文件等。

    - 相關方法

        - getParent() 返回該類加載器的父類加載器。

        - loadClass(String name) 加載名稱爲name的類,返回的結果是java.lang.Class類的實例。

           . 此方法負責加載指定名字的類,首先會從已加載的類中去尋找,若是沒有找到;從parent ClassLoader[ExtClassLoader]中加載;若是沒有加載到,則從Bootstrap ClassLoader中嘗試加載(findBootstrapClassOrNull方法),若是仍是加載失敗,則本身加載。若是還不能加載,則拋出異常ClassNotFoundException。

            . 若是要改變類的加載順序能夠覆蓋此方法;

        - findClass(String name) 查找名稱爲name的類,返回的結果是java.lang.Class類的實例。

        - findLoadedClass(String name) 查找名稱爲name的已經被加載過的類,返回的結果是java.lang.Class類的實例。

        - defineClass(byte[] b, int off, int len) 把字節數組b中的內容轉換成Java類,返回的結果是java.lang.Class類的實例。

        - resolveClass(Class<?> c) 連接指定的Java類

6、類加載器的代理模式

    代理模式

        - 交給其餘加載器來加載指定的類

    雙親委託機制

        - 就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依此追溯,直到最高的爺爺輩的,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。

        - 雙親委託機制是爲了保證Java核心庫的類型安全。

            . 這種機制就保證不會出現用戶本身能定義java.lang.Object類的狀況。

        - 類加載器除了用於加載類,也是安全的最基本的屏障。

        - 雙親委託機制只是代理模式的一種

            . 並非全部的類加載器都採用雙親委託機制。

            . tomcat服務器類加載器也使用代理模式,所不一樣的是它是首先嚐試去加載某個類,若是找不到在代理給父類加載器。這與通常類加載器的順序是相反的。

相關文章
相關標籤/搜索