JVM類加載過程詳細分析

2020-04-12 16:07:22來源:博客園 閱讀 ()

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

JVM類加載過程詳細分析

雙親委派加載模型

為什么需要雙親委派加載模型

主要是為了安全,避免用戶惡意加載破壞JVM正常運行的字節碼文件,比如說加載一個自己寫的java.util.HashMap.class。這樣就有可能造成包沖突問題。

類加載器種類

file

  • 啟動類加載器:用于加載jdkrt.jar的字節碼文件
  • 擴展類加載器:用于加載jdk/jre/lib/ext文件夾下的字節碼文件
  • 應用程序類加載器:加載classPath下的字節碼文件
  • 自定義類加載器:用戶在程序中自己定義的加載器

源碼分析

1、ClassLoader.loadClass()

    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);
            // 如果這個Class對象還沒有被加載,下面就準備加載
            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
                }
				// 如果父類加載器也沒有加載這個Class對象,就由自己來加載
                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;
        }
    }

不遵守雙親委派加載模型的例子

雙親委派加載模型僅僅是一個約定,后面實現類加載器時,是可以不遵守這個約定。ClassLoader是在JDK1.0的時候就設計好的,而雙親委派加載模型在JDK1.2引入的。所以,有些機制是沒有遵守這個約定的。比如:Service Provider Interface機制的JDBC就沒有遵守這個約定。

1、為什么JDBC無法遵守這個約定?
JDBCSPI機制的一個例子,JDK定義了java.sql.Connection核心接口,后續MySQLOracle為其提供實現類。在運行中是通過java.sql.DriverManager來獲取指定實現類的實例。這里需要明白三個問題:

  • java.sql.DriverManager是在rt.jar中,由核心類加載器加載的;
  • 第三方所提供Collection的實現類都是在classpath中;
  • 類中方法想加載新的字節碼文件時,其初始類加載器就是當前這個類的定義類加載器;

也就是說當JVMjava.sql.DriverManager類的getConnection()方法中獲取Collection實現類的字節碼時,當前類的定義類加載器是啟動類加載器,而按照約定啟動類加載器是不允許加載classpath下的字節碼。所以,JDBC就無法遵守這個約定。

2、JDBC是如何解決上面的問題的?
為了解決這個,java在線程中放入一個類加載器Thread.currentThread().getContextClassLoader();而這個類加載器可以是隨意的。比如你想加載classpath包下的字節碼文件,只需要設置當前線程的類加載器為應用程序類加載器即可。

JVM類加載過程

JVM本質的工作就是讀取字節碼文件、執行字節碼文件中的指令。其中JVM將讀取字節碼文件的過程稱為JVM類加載過程。

JVM讀取的字節碼文件將放在方法區里;

JVM類加載機制分為五個部分:加載、驗證、準備、解析、初始化。如下圖所示:
file

一、Loading:加載

這一步是將JVM外的字節碼文件加載到JVM內部方法區中的Class對象。

JVM可以通過幾種方式來加載外部的字節碼文件?

  • 從本地讀字節碼文件;
  • 從網絡讀取字節碼文件;
  • 通過動態生成的字節碼文件;

初始類加載器和定義類加載器

由于雙親委派加載模型的存在,一個Class對象的初始類加載器initiating class loader和定義類加載器defining class loader有可能不是同一個。

  • 初始類加載器:它是指讓JVM加載這個字節碼文件
  • 定義類加載器:它是真正調用defineClass方法,將字節碼轉換成Class對象

java在判斷instanceof時,只有類名、defining class loader都相等,才表示是同一個類的實例。

Class.getClassLoader()得到的是定義類加載器

相關實驗代碼

1、驗證使用不同ClassLoader加載字節碼文件

// 這種方法是不遵守雙親委派加載模型的約定
public class ClassLoaderLoading {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 這個Class對象是由當前方法的類加載器加載
        Class c1 = MiniJVM.class;
        Class c2 = new MyClassLoader().loadClass("com.github.hcsp.MiniJVM");
        // 使用c2創建一個MiniJVM實例
        Object o = c2.getConstructor().newInstance();
        System.out.println(o instanceof MiniJVM);
        MiniJVM demo = (MiniJVM) o;
    }

    private static class MyClassLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            if (name.contains("MiniJVM")) {
                try {
                    byte[] bytes = Files.readAllBytes(new File("target/classes/com/github/hcsp/MiniJVM.class").toPath());
                    return defineClass(name, bytes, 0, bytes.length);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                return super.loadClass(name);
            }
        }
    }
}

2、實現一個遵守雙親委派加載模型的類加載器

public class ClassLoaderLoading {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = MiniJVM.class;
        Class c2 = new MyClassLoader(ClassLoader.getSystemClassLoader()).loadClass("com.github.hcsp.MiniJVM");
        System.out.println("c2 = " + c2);
    }

    private static class MyClassLoader extends ClassLoader {
        public MyClassLoader(ClassLoader systemClassLoader) {
            super(systemClassLoader);
        }
        
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            // 加載你想讓這個類加載器加載的字節碼文件
            if (name.contains("MiniJVM")) {
                try {
                    byte[] bytes = Files.readAllBytes(new File("target/classes/com/github/hcsp/MiniJVM.class").toPath());
                    return defineClass(name, bytes, 0, bytes.length);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                // 其他的字節碼文件交由父類加載器加載
                return super.loadClass(name);
            }
        }
    }
}

二、Linking:鏈接

當一個.java文件編譯成.class文件時,里面含有一個符號引用,比如/java/utils/HashMapLinking是指將這符號引用與具體的class對象鏈接起來。

每個字節碼結構都有一個運行時常量池,它會存儲每個符號引用和所對應的具體對象,以此實現鏈接。

  • Verification:驗證字節碼的正確性
  • Preparation:為static成員賦默認初始值
  • Resolution:解析當前字節碼里包含的其他符號引用

三、Initializing

執行初始化方法。比如下面的四個虛擬機指令:newgetstaticputstaticinvokestatic

原博客地址


原文鏈接:https://www.cnblogs.com/fourther/p/12687964.html
如有疑問請與原作者聯系

標簽:AV使用COM定義程序解決

版權申明:本站文章部分自網絡,如有侵權,請聯系:west999com@outlook.com
特別注意:本站所有轉載文章言論不代表本站觀點,本站所提供的攝影照片,插畫,設計作品,如需使用,請與原作者聯系,版權歸原作者所有

上一篇:Spring Cloud Zuul 路由網關及服務過濾

下一篇:Spring Cloud Config 分布式配置中心

宅男午夜福利美女来袭