JVM 类加载机制 学习记录 1

java com.yyds.jvm.Math.class 运行Math 类的过程如下:

启动Java.exe,调用底层 C++ 库 来创建 Java 虚拟机(C++实现),然后创建一个引导类加载器(C++实现),再调用Java代码创建JVM 启动器实例:sun.misc.Launcher 的实例, 此实例初始化的过程中,会创建其他的类加载器,包括扩展类加载器(ExtClassLoader) 和 应用类加载器(AppClassLoader), 使用类加载器加载要运行的类Math, 加载完成后执行 Math.main() 方法,运行结束,JVM销毁。

 

 

其中Java 部分的步骤如下:

1、创建Launcher(单例)实例,创建其他加载器

源码如下:

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

        try {
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);  //创建AppClassLoader 对象(单例),并制定 var1(ExtClassLoader) 为parent属性
        } 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);
        }
    }
static class ExtClassLoader extends URLClassLoader {
        private static volatile Launcher.ExtClassLoader instance;

        public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
            if (instance == null) {
                Class var0 = Launcher.ExtClassLoader.class;
                synchronized(Launcher.ExtClassLoader.class) {
                    if (instance == null) {
                        instance = createExtClassLoader();
                    }
                }
            }

            return instance;
        }

        private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {
            try {
                return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
                    public Launcher.ExtClassLoader run() throws IOException {
                        File[] var1 = Launcher.ExtClassLoader.getExtDirs();
                        int var2 = var1.length;

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

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

        void addExtURL(URL var1) {
            super.addURL(var1);
        }

        public ExtClassLoader(File[] var1) throws IOException {
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);   // 第二个参数为null,是因为引导类加载器是C++实现的,此处没有,所以ExtClassLoader.getParent == null
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }
    ...
}
View Code
static class AppClassLoader extends URLClassLoader {  //继承URLClassLoader
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
            final String var1 = System.getProperty("java.class.path");  //扫描 java/class/path 路径下的字节码文件
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
                public Launcher.AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new Launcher.AppClassLoader(var1x, var0);
                }
            });
        }
    AppClassLoader(URL[] var1, ClassLoader var2) {
        super(var1, var2, Launcher.factory);
        this.ucp.initLookupCache(this);
    }
    ... 
}
View Code 

2、类加载器 加载 Math 类:

  从上图可看到把当前线程类加载器设置为了AppClassLoader, 所以此处会先调用 AppClassLoader 类的 loadClass 方法加载 Math 类,源码如下, 经过判断后,如果未加载

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));
                }
            }
            if (this.ucp.knownToNotExist(var1)) {    
                Class var5 = this.findLoadedClass(var1);  //判断类是否已加载
                if (var5 != null) {
                    if (var2) {
                        this.resolveClass(var5);
                    }
                    return var5;
                } else {
                    throw new ClassNotFoundException(var1);
                }
            } else {
                return super.loadClass(var1, var2);  //正常情况调用父类URLClassLoader 的 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);   //判断是否已加载
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);   //调用父加载器的loadclass方法,如果父加载器不为null,则一直向上调用  双亲委派机制实现
                    } else {
                        c = findBootstrapClassOrNull(name);     //因为扩展类加载器的parent是null,代表引导类加载器加载类
                    }
                } 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;
        }
    }

 

// ClassLoader.java   没有具体实现,需要看到对应的子类URLClassLoader里的实现
protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }


// URLClassLoader.class
protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
        final Class<?> result;
        try {
            result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");    //根据全限定类名获取路径
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }

3、 调用入口 main 方法

 

双亲委派机制:

其中双亲委派机制即是: 当前类加载器去加载类的时候,如果发现类没有被加载,则优先去使用父加载器去加载,依次直到引导类加载器,如果引导类加载器没有加载到类,则依次向后再去加载,直到加载到类。

打破双亲委派机制: 例如 tomcat 打破双亲委派机制。 

  为什么要打破双亲委派机制:  当一个类的不同版本使用同一个限定类名都时候,如果其中一个项目加载了1.x版本的类, 则另外的项目无法再加载 2.x 版本的类,会导致问题。

  使用自定义类加载器打破双亲委派机制, 指定自定义类加载器扫描的路径,重写 loadclass 方法,当调用非Java 本身类的时候,加载Class 去掉父加载优先的规则,直接使用当前自定义类加载器去加载。

posted @ 2022-03-11 17:47  长弓射大狗  阅读(26)  评论(0编辑  收藏  举报