计算机系统应用教程网站

网站首页 > 技术文章 正文

java类机制加载制度深度解析(JVM基础篇)

btikc 2024-11-02 11:09:03 技术文章 3 ℃ 0 评论

引言

目前工作上大部分都是springboot项目,启动的时候都只需要简单地运行main方法就可以进行启动了。不过当我们用java -jar命令运行启动类的main方法进行启动时,对应的类加载机制以及运行过程是怎么样的呢? (●ˇ?ˇ●) 容我娓娓道来

java类的加载运行过程

当我们用java命令运行main方法时,类加载器会将主类优先加载至JVM中,在主类的运行过程中,类加载器会根据主类的需要,逐步的将使用到的类加载至JVM中。

注意:java中的类并不是在jar包启动时全部加载,而是在主类的运行过程中,使用到才会进行加载,很重要

java类的加载到执行过程大概分为以下几步:

  1. 加载:通过io流的方式读取硬盘上的class文件,并且将mian方法以及main方法中使用到的类进行加载,并验证class文件中的字节码是否正确
  2. 准备:给类的静态变量分配内存,并赋予默认值(看到这个就知道静态变量的加载优先级了O(∩_∩)O),例如int类型默认值为0,对象的默认值为null等
  3. 解析:该阶段会将静态方法名替换为指向数据存储的指针,即静态链接过程
  4. 初始化:对类的静态变量初始化为指定的值,并执行静态代码块

类的整体加载流程如下:

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

java类加载器以及其双亲委派机制

  • java的类加载器分为以下四类:
  1. 启动类加载器(使用的是C语言写的,java中不可见):负责加载jvm运行时需要的核心包,比如javaws.jar、rt.jar,java.lang.String、Long这些java类都是此加载器进行加载
  2. 扩展类加载器(ExtClassLoader):负责加载加载jvm运行的扩展包,主要是我们jre/ext文件夹下面的包,这些包应该是java的开发人员当年不敢改动核心包(开发扩展包和核心包的开发人员估计是两个部门的吧吧哈哈哈),又必须加扩展功能时,扩展出来的包
  3. 应用类加载器(AppClassLoader):这个就主要是负责加载我们写的那些业务代码啦,加载的是项目classpath目录下的class文件
  4. 自定义加载器:当以上的三种加载器满足不了我们的要求的时候(比如加载的class文件不在classspath目录下),就需要自定义类加载器来加载了
//加载器示例        
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader extClassloader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassloader.getParent();
System.out.println("the bootstrapLoader : " + bootstrapLoader);
System.out.println("the extClassloader : " + extClassloader);
System.out.println("the appClassLoader : " + appClassLoader);
  • 双亲委派机制

java类加载是存在一种亲子结构的,专业属于双亲委派机制,即类加载器需要加载某个类时,会不断的委托给自己的父类进行类加载工作,直到找到此工作委托给最顶级的类(不存在父类),此时才会开始正式加载类,当顶级类加载不了对应的类后,会不断的委托给自己的子类进行加载,直到加载到目标类为止。语言比较干涩,通过AppClassLoader的源码(省略了和机制无关的代码)来解释一下就清楚了:

//ClassLoader的loadClass方法,里面实现了双亲委派机制
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 检查当前类加载器是否已经加载了该类
        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) {}
            if (c == null) {
                //这里可以看到,如果根加载器和父加载器都无法加载目标类时,就会在当前classpath下寻找目标类
                c = findClass(name);
            }
        }
        if (resolve) {  //不会执行
            resolveClass(c);
        }
        return c;
    }
}
  • 双亲委派机制的目的
  1. 保证沙箱安全:java的核心类库不会被修改,保证了核心api功能的正常
  2. 避免类重复加载:父加载器加载过的内容,子加载器无需加载,保证了类的唯一性

自定义类加载器

如果需要自定义类加载器需要继承Classloader类,可以重写findClass(可以自定义从指定的路径读取类文件)、loadClass(可以定义类加载方式,Classloader中的loadclass方法遵循的是双亲委派机制,如果需要打破此机制,需要重写这个方法)

下图中我自定义了loadclass方法,成功加载了非本地classpath下的class文件

现在我们开始打破双亲委派机制,即使当前项目的classpath存在此class,还是加载指定目录下的class文件,如下图:



以上就是java类机制加载制度的所有内容啦,下一篇为:JVM整体结构解析(包括jvm内存结构) ~

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表