手撸jdk源码分析类加载机制

2022/2/20 9:26:23

本文主要是介绍手撸jdk源码分析类加载机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

  我们一般写的java文件jvm是识别不了的,因此需要编译,编译后会变成.class文件,而要执行代码,jvm首先会去加载.class文件到内存中,那么他的流程是什么样的呢:

  1.首先肯定创建java虚拟机(就像我们运行windows的程序首先得安装windows系统一个道理) 

  2.同时会创建一个引导类加载器实例(这个由c++实现,也就是我们的BootStrap类加载器)

  3.然后c++代码会调用创建java的启动类launcher创建类加载器,去加载不同类型的类

  4.类加载完成会调用对应的main方法

  5.运行后销毁

流程如下图所示:

 

 

 具体的类加载流程:加载>>验证>>准备>>解析>>初始化>>使用>>卸载    

1.加载:加载就是找到对应的字节码文件并加载到内存

2.验证: 验证字节码文件的格式等正确性

3.准备:加载静态变量到内存并赋值

4.解析:将符号引用转换为直接引用(内存地址)

5.初始化: 将类加载到方法区,执行静态代码块,初始化静态变量值

大概了解之后,我们直接撸代码:

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 {
            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);
        }

    }
    
    
    public ExtClassLoader(File[] var1) throws IOException {
            super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);//这里可以看到ext的父类加载器设为null
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }
        
    AppClassLoader(URL[] var1, ClassLoader var2) {  //这里可以看到app的父类加载器就是上面传进来的var1->extClassLoader
            super(var1, var2, Launcher.factory);
            this.ucp.initLookupCache(this);
        }

这就可以看到应用类加载器的父类加载器是扩展类加载器,继续看加载流程,看appClassLoader的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));
                }
            }

            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);
            }
        }

再看父类,extClassLoader调用的也是这个:

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);  //查询JVM,已经被加载并返回,否则返回null
            if (c == null) { //不存在
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {  //有父调用父类加载器
                        c = parent.loadClass(name, false);
                    } else { //当父类加载器不存在,如extClassLoad设置父类为null(其实是有的,但其父类bootStrapClassLoad在JVM中由c++实现,所以设置为null)
                        c = findBootstrapClassOrNull(name); //这里点进去看可以发现其实就是获取BootStrapClass
                    }
                } 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;
        }
    }

这里我们可以理一下顺序:

1.首先会去调用appClassLoad的loadClass方法,如果已经加载过,则直接获取,否则调用父加载方法

2.父类方法依旧会找一次当然还是没有,会去调用父类加载器的loadClass方法(前面我们已经知道appClassLoad父加载器为extClassLoad)

3.递归调用classLoad方法,直到父加载器为空(也就是extClassLoad),会去获取BootStrapClass类加载器调用类加载方法

4.还是获取不到则按顺序查找类,这个我们下面详细再看

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"); //拼接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;
    }

其实就是根据类名拼接class文件地址,并解析指定的资源类,再往下基本就是分析c++代码了,这个后续有时间在研究吧.

看完代码,我们再来看一些我们之前背了又忘的结论:双亲委派机制是不是就很容易理解了

 



这篇关于手撸jdk源码分析类加载机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程