java 类加载器 双亲委派 根加载器、扩展类加载器、系统类加载器详解编程语言

前言

以下所述,基于java8所写;类加载器通过完全限定名(比如,com.mypak.module1.MyClass)来加载类,它使类可以动态加载类到jvm中,java并未规定类的位置,可以来自本地文件系统,也可以来自网络。

加载器分类

类加载器分为根加载器(bootstrap classloader)扩展类加载器(ext classloader)系统类加载器(system classloader)自定义类加载器(通常继承java.net.URLClassLoader,重写findClass()),它们的关系通常如下。
在这里插入图片描述
从图中可以看出,每个类加载器都有一个父加载器(加载器有引用指向父加载器,而不是继承),在加载类时,首先check本身是否已加载,如果已加载,则返回,如果未加载,如果有父加载器则交给父加载器,如果没有父加载器则使用根加载器,如果仍没找到,则本加载器加载类,每一级加载器都执行相同的操作,这种机制称为委托机制,英语是parents delegation model,翻译过来是双亲委派机制(其实有点词不达意)。

  • 由于双亲委派机制,加载java.lang.String 时会一直往上委派,直到根加载器,而根加载器只会加载java_home/jre/lib/rt.jar中的java.lang.String,从而确保自定义的java.lang.String不会加载到jvm中,而不会让jvm错乱。
  • 类加载器+类的完全限定名,组成了在jvm中的唯一标识,如果类加载器不一样,即使类限定名相同,也不相等。

类加载器用抽象类java.lang.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); //首先,check自身加载器是否已加载目标类 
            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); //调用findClass()方法 
 
                    // 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; 
        } 
    } 

根加载器

根加载器主要是用来加载java_home/jre/lib下的jar包,比如rt.jar(含有全部java api的类),根加载器用C/C++实现,用null表示,在java代码中无法获取到根加载器。
在这里插入图片描述
rt.jar如下
在这里插入图片描述

扩展类加载器

用来加载System.getproperty("java.ext.dirs")也就是java_home/jre/lib/ext`下的jar包,扩展类加载器的父加载器是根加载器。
在这里插入图片描述

系统类加载器

用来加载System.getproperty("java.class.path")也就是我们常说的classpath下的类,此路径下都是应用程序的类,所以也可称为应用程序类加载器,它的父加载器是扩展类加载器,classLoader.getSystemClassLoader()返回的就是系统类加载器

自定义类加载器

在程序运行时,如需自定义类加载器,通常继承java.net.URLClassLoader,重写findClass方法,这样符合双亲委派机制。
在这里插入图片描述

实例

系统类加载器–>扩展类加载器–>根加载器

public class MyClassLoader { 
    public static void main(String[] args) { 
        // 系统类加载器(应用类加载器) 
        System.out.println(MyClassLoader.class.getClassLoader()); 
 
         //扩展类加载器 
        System.out.println(MyClassLoader.class.getClassLoader().getParent()); 
        //系统类加载器 
        System.out.println(MyClassLoader.class.getClassLoader().getParent().getSystemClassLoader()); 
         
		//根加载器 
        System.out.println(MyClassLoader.class.getClassLoader().getParent().getParent()); 
 
		//扩展类加载器,java_home/jre/lib/ext/dnsns.jar 
        System.out.println(DNSNameService.class.getClassLoader()); 
 
		//根加载器 
        System.out.println(String.class.getClassLoader()); 
    } 
} 

输出

[email protected] 
[email protected] 
[email protected] 
null 
[email protected] 
null 

查看源码得知,ExtClassLoader加载System.getProperty("java.ext.dirs")路径下的类,在我的电脑上是D:/jdk1.8.0_211/jre/lib/ext;

 static class ExtClassLoader 
    extends URLClassLoader 
  { 
	 private static File[] getExtDirs() 
	    { 
	      String str = System.getProperty("java.ext.dirs"); 
	     //省略 
	    } 
    } 

AppClassLoader源码得知,它加载System.getProperty("java.class.path")下的类,在我的系统上是D:/jdk1.8.0_211/jre/lib/ext;D:/jdk1.8.0_211/jre/lib/;还有应用的类,可以看到它包含了ExtClassLoader的加载路径。

static class AppClassLoader 
    extends URLClassLoader 
  { 
	 public static ClassLoader getAppClassLoader(final ClassLoader paramClassLoader) 
	      throws IOException 
	    { 
	      String str = System.getProperty("java.class.path"); 
	 		//省略 
	    } 
} 

类加载器+类全限定名在jvm中是类的唯一标识

public class MyClassLoader extends ClassLoader{ 
    @Override 
    protected Class<?> findClass(String name) throws ClassNotFoundException { 
        String fileName = name.substring(name.lastIndexOf(".")+1) + ".class"; 
        InputStream is = getClass().getResourceAsStream(fileName); 
        assert is != null; 
        byte[] b; 
        try { 
            b = new byte[is.available()]; 
            is.read(b); 
            return defineClass(name,b,0,b.length); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
        return null; 
 
 
    } 
 
    public static void main(String[] args) { 
        try { 
            Class<?> aClass = new MyClassLoader().findClass("com.jun.javase.MyClassLoader"); 
            System.out.println(aClass+"---"+MyClassLoader.class); 
            System.out.println(aClass.getClassLoader()+"---"+MyClassLoader.class.getClassLoader()); 
            System.out.println(MyClassLoader.class == aClass); 
        } catch (ClassNotFoundException e) { 
            e.printStackTrace(); 
        } 
    } 
} 

输出

class com.jun.javase.MyClassLoader---class com.jun.javase.MyClassLoader 
[email protected][email protected] 
false 

输出false,因为类加载器不同,即使同一个类,也不相等。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/20254.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论