1.JVM常识之 类加载器

1.jvm组成

在这里插入图片描述
JVM组成:
1.类加载器
2.运行时数据区
3.执行引擎
4.本地库接口

各组件的作用:
首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

2.类加载器

下图借用图灵 诸葛老师的流程图,respect!!!
在这里插入图片描述
2.1 java里的几种类加载器

BootstrapClassLoad 系统级类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库。是C或者C++ 生成的对象。创建了Launcher类
extClassloader 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的Jar类包。属于Launcher类构造方法生成
AppClassload应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载开发自己写的那些类。属于Launcher类构造方法生成
也可以自定义加载器:负责加载开发者自定义路径下的类包。

Launcher 类部分源码
在这里插入图片描述

package sun.misc;

public class Launcher {
    private static Launcher launcher = new Launcher();
    private static String bootClassPath = System.getProperty("sun.boot.class.path");
    private ClassLoader loader;
   
    public static Launcher getLauncher() {
        return launcher;
    }

    public Launcher() {
        ExtClassLoader var1;
        try {
        //extClassloader 扩展类加载器 获取,在getExtClassLoader()里没有ExtClassLoader 的话,会new ExtClassLoader()
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
         //AppClassload应用程序类加载器获取,在getAppClassLoader()里没有AppClassLoader的话,会new AppClassLoader()。AppClassLoader是应用级默认的类加载器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
    }
}

2.2类加载过程
注:主类在运行过程中如果使用到其它类,会逐步加载这些类。jar包或war包里的类不是一次性全部加载的,是使用到时才加载。懒加载模式。
主要流程:
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载

加载:根据类文件路径找到相应的class文件导入
检测:校验字节码class文件的正确性。都是cafe babe 开头的字节码文件。
准备: 给类中的静态变量分配内存空间,并赋予默认值 eg:boolean false int 0
解析:jvm将常量池中的符号引用替换成直接引用的过程
初始化:对静态变量和静态代码块执行初始化工作。

2.3ClassLoader 类加载过程源码
类加载过程都在根加载器抽象类:ClassLoader 里有定义或者实现
该ClassLoader 类有两个核心方法
loadClass(String, boolean),实现了双亲委派机制,
还有一个方法是findClass(),默认实现是空方法。根据要加载的class类路径去实际加载类

package java.lang;
public abstract class ClassLoader {
	private final ClassLoader parent;
  /**
   * 双亲委派加载方式的保证
   */
	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 {
                        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;
        }
    }
    
    //由子类实现的实际加载类的操作 实际实现是URLClassLoader类
	protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
}

URLClassLoader类 是AppClassLoader 和 ExtClassLoader 的父类。
Launcher类中的静态内部类AppClassLoader部分核心源码

 static class AppClassLoader extends URLClassLoader {
        final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

        public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
          //AppClassLoader 加载的是应用ClassPath路径下的类包
            final String var1 = System.getProperty("java.class.path");
            final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
            return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {
                public AppClassLoader run() {
                    URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
                    return new AppClassLoader(var1x, var0);
                }
            });
        }

         //实际加载类调用的是父类URLClassLoader类中的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 {
             //调用的是父类URLClassLoader类中的loadClass
                return super.loadClass(var1, var2);
            }
        }
    }

Launcher类中的静态内部类ExtClassLoader部分核心源码

static class ExtClassLoader extends URLClassLoader {
        private static volatile ExtClassLoader instance;

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

            return instance;
        }

        private static ExtClassLoader createExtClassLoader() throws IOException {
            try {
                return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {
                    public 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 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);
            SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
        }

        private static File[] getExtDirs() {
        //这里可以看出拓展类加载器 加载的是jre中ext内的相关类
            String var0 = System.getProperty("java.ext.dirs");
            File[] var1;
            if (var0 != null) {
                StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
                int var3 = var2.countTokens();
                var1 = new File[var3];

                for(int var4 = 0; var4 < var3; ++var4) {
                    var1[var4] = new File(var2.nextToken());
                }
            } else {
                var1 = new File[0];
            }

            return var1;
        }
    }

总结:
ClassLoad 抽象类里面2个核心方法:
loadClass()实现了双亲委派机制
findClass() 根据对应的类路径加载类

3.类加载器中的双亲委派机制

在这里插入图片描述
双亲委派:加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

3.1为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改,安全性保证。
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性。

3.2自定义类加载器(父加载器是appClassLoad)
自定义类加载器只需要继承 java.lang.ClassLoader 抽象类,重写该类有两个核心方法,
loadClass(),实现了双亲委派机制,findClass(),默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。

3.3 自定义类加载器打破双亲委派机制
重写继承 java.lang.ClassLoader 抽象类中的loadClass()即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值