类加载系统

类加载时机

  • new、getstatic、putstatic、invokestatic命令初始化类时

  • reflect包反射调用类时

  • 初始化类时,父类未初始化,要先初始化父类

  • 启动Java程序时需要指定主类(Main),虚拟机会执行该类的初始化

类加载过程

加载->验证->准备->解析->初始化->使用->卸载

加载:通过全限定类型和对应的classpath及Java环境变量目录,将二进制class文件加载到方法区

初始化:初始化创建字节码class对象

类加载器

类加载.jpg
  • Bootstrap ClassLoader(启动类加载器):加载JAVA_HOME\lib目录或-Xbootclasspath指定的虚拟机认可的类

  • Extension ClassLoader(扩展类加载器):加载JAVA_HOME\lib\ext目录或java.ext.dirs指定的类库

  • Application ClassLoader(应用类加载器):负责加载classpath目录下的类库

  • User ClassLoader(用户自定义类加载):加载应用外的类文件,e.g.JRebel

自定义类加载器案例

继承ClassLoader抽象类后,因历史原因需要兼容,所以提供重写loadClass、findClass来实现指定类文件加载,建议重写findclass方法

public class CustomClassLoader extends ClassLoader {

    private String classpath;

    public CustomClassLoader(String classpath) {
        this.classpath = classpath;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundExc        String path = classpath + File.separator + name.replace(".", File.separator) + ".class";
        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] bytes = new byte[2048];
            int len = 0;
            while ((len = is.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            byte[] classData = bos.toByteArray();
            return defineClass(name, classData, 0, classData.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.loadClass(name);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = classpath + File.separator + name.replace(".", File.separator) + ".class";

        try {
            InputStream is = new FileInputStream(path);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] bytes = new byte[2048];
            int len = 0;
            while ((len = is.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            byte[] classData = bos.toByteArray();
            return defineClass(name, classData, 0, classData.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return super.findClass(name);
    }
}
class CustomClassLoaderTest {

    @Test
    void findClass() throws Exception {
        CustomClassLoader customClassLoader = new CustomClassLoader("C:\\Users\\80604\\Desktop");
        Class<?> c = customClassLoader.loadClass("com.zhaoccf.study.jvm.classloader.Test");
        if (c!=null){
            Object obj = c.newInstance();
            Method method = c.getMethod("say");
            method.invoke(obj,null);
            System.out.println(obj.getClass().getClassLoader());
            System.out.println(c.getClassLoader());
        }
    }

    @Test
    void loadClass() throws Exception {
        CustomClassLoader customClassLoader = new CustomClassLoader("C:\\Users\\80604\\Desktop");
        Class<?> c = customClassLoader.loadClass("java.lang.String");
        if (c!=null){
            Object obj = c.newInstance();
            Method method = c.getMethod("say");
            method.invoke(obj,null);
            System.out.println(obj.getClass().getClassLoader());
            System.out.println(c.getClassLoader());
        }
    }
}

执行结果

// findClass
Hello CustomClassLoader!
com.zhaoccf.study.jvm.classloader.CustomClassLoader@7ff2a664
com.zhaoccf.study.jvm.classloader.CustomClassLoader@7ff2a664

Process finished with exit code 0
//loadclass

java.lang.NoSuchMethodException: java.lang.String.say()

    at java.lang.Class.getMethod(Class.java:1786)
    at com.zhaoccf.study.jvm.classloader.CustomClassLoaderTest.loadClass(CustomClassLoaderTest.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)


Process finished with exit code -1

双亲委派与打破双亲委派

当类加载器收到加载任务时,优先交给父类加载器去完成,只有当父类加载器无法完成加载时子类才会尝试加载该类

// java.lang.ClassLoader#loadClass(java.lang.String, boolean)
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 {
                    //如果父类加载器为空则启动类加载器加载,启动类加载器不是Java实现所以为null
                    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;
    }
}

为什么使用双亲委派机制?

  • 安全性,避免用户自定义类覆盖Java原始核心类

  • 避免重复加载,父类加载过的子类无需加载

为什么打破双亲委派?

某些情况下,父类需要加载的class文件受加载范围限制,无法加载,只能委托子类加载器进行加载

怎么打破双亲委派?

  1. Java SPI

ServiceLoader内部类LazyIterator加载META-INF/services/类全限定类名文件

SPI.png

java.sql.Driver在rt.jar里应该由启动类加载器来加载,现在由ServiceLoader存储在ContextClassLoader里的应用类加载器来加载对应厂商的实现

public class DriverLoad {
    public static void main(String[] args) {
        ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
        Iterator<Driver> iterator = loader.iterator();
        if (iterator.hasNext()) {
            Driver driver = iterator.next();
            System.out.println("driver:"+driver.getClass()+"classloader:"+driver.getClass().getClassLoader());
        }
        System.out.println("thread context loader:"+Thread.currentThread().getContextClassLoader());
        System.out.println("serviceloader:"+ServiceLoader.class.getClassLoader());
    }
}

执行结果

driver:class com.mysql.jdbc.Driverclassloader:sun.misc.Launcher$AppClassLoader@18b4aac2
thread context loader:sun.misc.Launcher$AppClassLoader@18b4aac2
serviceloader:null

Process finished with exit code 0
  1. Dubbo增强SPI

  2. Tomcat加载多个应用程序

  3. 热加载技术(devtools、JRebel、OSGi等)

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容