日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

URLClassLoader詳解

作者:Jothan Zhong 更新時間: 2024-01-16 編程語言

URLClassLoader詳解

ClassLoader翻譯過來就是類加載器,普通的java開發者其實用到的不多,但對于某些框架開發者來說卻非常常見。理解ClassLoader的加載機制,也有利于我們編寫出更高效的代碼。ClassLoader的具體作用就是將class文件加載到jvm虛擬機中去,程序就可以正確運行了。但是,jvm啟動的時候,并不會一次性加載所有的class文件,而是根據需要去動態加載。想想也是的,一次性加載那么多jar包那么多class,那內存不崩潰。本文的目的也是學習ClassLoader這種加載機制。。

  • java類加載機制原理
  • 雙親加載機制的優劣

java類加載機制

ClassLoader結構


ClassLoader結構

java中內置了很多類加載器,本文只討論幾個核心類加載器:

ClassLoader:所有類加載器的基類,它是抽象的,定義了類加載最核心的操作。所有繼承與classloader的加載器,都會優先判斷是否被父類加載器加載過,防止多次加載,防止加載沖突。。。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
    //鎖,防止多次加載,所以jvm啟動巨慢
        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;
        }
    }

jdk 1.7為了提供并行加載class,提供ClassLoader.ParallelLoaders內部類,用來封裝一組并行能力的加載器類型。這個一般是用不到的,有興趣可以先看一下。但是需要知道ClassLoader是支持并行加載的。

    private static class ParallelLoaders1

Bootstrap classLoader:位于java.lang.classload,所有的classload都要經過這個classload判斷是否已經被加載過,采用native code實現,是JVM的一部分,主要加載JVM自身工作需要的類,如java.lang._、java.uti._等; 這些類位于$JAVA_HOME/jre/lib/rt.jar。Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類,底層由C++編寫,已嵌入到了JVM內核當中,當JVM啟動后,Bootstrap ClassLoader也隨著啟動,負責加載完核心類庫后,并構造Extension ClassLoader和App ClassLoader類加載器。

  /**
     * Returns a class loaded by the bootstrap class loader;
     * or return null if not found.
     */
    private Class<?> findBootstrapClassOrNull(String name)
    {
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
    }

    // return null if not found
    private native Class<?> findBootstrapClass(String name);

SecureClassLoader:繼承自ClassLoader,添加了關聯類源碼、關聯系統policy權限等支持。

    public class SecureClassLoader extends ClassLoader1

URLClassLoader:繼承自SecureClassLoader,支持從jar文件和文件夾中獲取class,繼承于classload,加載時首先去classload里判斷是否由bootstrap classload加載過,1.7 新增實現closeable接口,實現在try 中自動釋放資源,但撲捉不了.close()異常

public class URLClassLoader extends SecureClassLoader implements Closeable1

ExtClassLoader:擴展類加載器,繼承自URLClassLoader繼承于urlclassload,擴展的class loader,加載位于$JAVA_HOME/jre/lib/ext目錄下的擴展jar。查看源碼可知其查找范圍為System.getProperty(“java.ext.dirs”)。

public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            //System.getProperty("java.ext.dirs");
            //在項目啟動時就加載所有的ext.dirs目錄下的文件,并將其初始化
            final File[] var0 = getExtDirs();

            try {
            //AccessController.doPrivileged特權,讓程序突破當前域權限限制,臨時擴大訪問權限
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        int var1 = var0.length;

                        for(int var2 = 0; var2 < var1; ++var2) {
                            MetaIndex.registerDirectory(var0[var2]);
                        }

                        return new Launcher.ExtClassLoader(var0);
                    }
                });
            } catch (PrivilegedActionException var2) {
                throw (IOException)var2.getException();
            }
        }

ExtClassLoader 比較有意思的是,他使用的是頂級類(classloader)的loadclass方法,并沒有重寫,而且他的父親加載器是null。。 。

public ExtClassLoader(File[] var1) throws IOException {
//parents is null
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }

AppClassLoader:應用類加載器,繼承自URLClassLoader,也叫系統類加載器(ClassLoader.getSystemClassLoader()可得到它),它負載加載應用的classpath下的類,查找范圍System.getProperty(“java.class.path”),通過-cp或-classpath指定的類都會被其加載,沒有完全遵循雙親委派模型的,它重的是loadClass方法

        public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
            int var3 = var1.lastIndexOf(46);
            if (var3 != -1) {
                SecurityManager var4 = System.getSecurityManager();
                if (var4 != null) {
                    var4.checkPackageAccess(var1.substring(0, var3));
                }
            }

            //ucp是SharedSecrets獲取的Java棧幀中存儲的類信息
            if (this.ucp.knownToNotExist(var1)) {
            //頂級類classloader加載的信息
                Class var5 = this.findLoadedClass(var1);
                if (var5 != null) {
                    if (var2) {
                    //link過程,Class載入必須link,link指的是把單一的Class加入到有繼承關系的類樹中,不link一切都無從談起了
                        this.resolveClass(var5);
                    }

                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                return super.loadClass(var1, var2);
            }
        }

Launcher:java程序入口,負責實例化相關class,ExtClassLoader和AppClassLoader都是其內部實現類。。。

首先獲取bootClassPath 的位置信息,交付于父類Classloader去加載位于$JAVA_HOME/jre/lib/rt.jar 的類

private static String bootClassPath = System.getProperty("sun.boot.class.path");
public static URLClassPath getBootstrapClassPath() {
        return Launcher.BootClassPathHolder.bcp;
    }

實例化相關ExtClassLoader和AppClassLoader,單線程運行,內部加載會對class文件加鎖(所以啟動慢的原因??)

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
        //實例化ExtClassLoader,
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
        //實例化AppClassLoader,并把ExtClassLoader置為父類
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        //為線程設置上下文加載器,防止線程多次加載類
        Thread.currentThread().setContextClassLoader(this.loader);
        //加載安全管理
        String var2 = System.getProperty("java.security.manager");
        if (var2 != null) {
            SecurityManager var3 = null;
            if (!"".equals(var2) && !"default".equals(var2)) {
                try {
                    var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
                } catch (IllegalAccessException var5) {
                    ;
                } catch (InstantiationException var6) {
                    ;
                } catch (ClassNotFoundException var7) {
                    ;
                } catch (ClassCastException var8) {
                    ;
                }
            } else {
                var3 = new SecurityManager();
            }

            if (var3 == null) {
                throw new InternalError("Could not create SecurityManager: " + var2);
            }

            System.setSecurityManager(var3);
        }

    }

ClassLoader運行結構

主要基于 雙親加載機制

classload運行結構

雙親加載機制:主要體現在ClassLoader的loadClass()方法中,思路很簡單:先檢查是否已經被加載過,若沒有加載則調用父類加載器的loadClass()方法,若父類加載器為空則默認使用啟動類加載器作為父類加載器。如果父類加載器加載失敗,拋出ClassNotFoundException異常后,調用自己的findClass()方法進行加載。

雙親與判斷等于:一般自定義的Class Loader可以從java.lang.ClassLoader繼承,不同classloader加載相同的類,他們在內存也不是相等的,即它們不能互相轉換,會直接拋異常。java.lang.ClassLoader的核心加載方法是loadClass方法

     Class  clazz = null; 
ClassLoader classLoader;
try {
classLoader =  new SpecialClassLoader ();
clazz = classLoader.loadClass("Hello");
System.out.println(clazz);
System.out.println(clazz.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class helloClazz;
helloClazz = Hello.class;
System.out.println("helloClazz:" + helloClazz);
System.out.println(helloClazz.getClassLoader());
System.out.println(helloClazz == clazz);
System.out.println(helloClazz.equals(clazz));

運行結果:

loadClass:Hello

specialLoadClass:test/Hello.class

loadClass:java.lang.Object

—resolveClass–

class com.test.javatechnology.classloader.test.Hello

com.test.javatechnology.classloader.SpecialClassLoader@106d69c

helloClazz:class com.test.javatechnology.classloader.test.Hello

sun.misc.Launcher$AppClassLoader@e2f2a

false

false

原文鏈接:https://blog.csdn.net/qq_43985303/article/details/127704907

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新