Java中的JVM虚拟机的类加载机制

虚拟机的类加载机制

这篇文章是学习《深入理解JAVA虚拟机》中的一些个人认为比较重要的知识点的总结

关于类的初始化

类的生命周期: 加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用和卸载(Using and Unloading)

企业微信截图_15725942678682.png

  1. 加载:获取类的二进制字节流

  2. 验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求

  3. 准备:为类变量分配内存并设置类变量初始值(不包含实例变量)

  4. 解析:将常量池内的符号引用替换为直接引用的过程

  5. 初始化:类加载过程的最后一步,开始执行类中定义的Java程序代码

  6. 使用:

  7. 卸载:

5种触发类进行初始化的场景:

  1. new(使用new初始化一个类)、getStatic\putStatic(获取或设置一个类的静态字段)、invokeStatic(调用一个类的静态方法)

  2. 使用java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先出发其初始化

  3. 初始化一个类的时候,如果发现其父类还没进行过初始化,则需要先触发其父类的初始化

  4. 虚拟机启动时,用户需要指定一个要执行的(包含main()方法的那个类),虚拟机会先初始化这个主类

其中一个demo

public class NotInitialization1 {
    public static void main(String[] args) {
        /**
         * 例子一:
         * 1. 读取非final修饰的静态字段时,如果类未被初始化则去初始化
         * 2. 当初始化一个类的时候,如果发现父类还未被初始化,则需要先出发父类的初始化
         * 3. 通过子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化
         * @param args
         */
        System.out.println(SubClass.value);

    }
}


public class SubClass extends SuperClass {
    static {
        System.out.println("SubClass init!");
    }
}

public class SuperClass {
    static {
        System.out.println("SuperClass init!");
    }
    public static int value = 123;

}
运行结果:
SuperClass init!
123

关于类加载器 Classloader

虚拟机设计团队把类加载阶段中“通过一个类的名称来获取描述此类的二进制字节流”,放到虚拟机外部去实现。这个代码模块成为“类加载器”。

注意: 即使两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类必定不相等。

import java.io.IOException;
import java.io.InputStream;

/**
 * 示例:
 *    即使这两个类都来源于同一个Class文件,但是属于两个不同的类加载器
 *    一个是由系统应用程序类加载器加载的,另一个由自定义的类加载器
 *
 * 结论:
 *    即使两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类必定不相等
 *
 */
public class ClassLoaderTest {

    public static void main(String[] args) throws Exception {

        //自定义一个类加载器
        ClassLoader myLoader = new ClassLoader() {
            /**
             * Loads the class with the specified <a href="#name">binary name</a>.
             * This method searches for classes in the same manner as the {@link
             * #loadClass(String, boolean)} method.  It is invoked by the Java virtual
             * machine to resolve class references.  Invoking this method is equivalent
             * to invoking {@link #loadClass(String, boolean) <tt>loadClass(name,
             * false)</tt>}.
             *
             * @param name The <a href="#name">binary name</a> of the class
             * @return The resulting <tt>Class</tt> object
             * @throws ClassNotFoundException If the class was not found
             */
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {

                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";

                    InputStream is = getClass().getResourceAsStream(fileName);

                    if (is == null) {
                        return super.loadClass(name);
                    }

                    byte[] b = new byte[0];

                    b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };

        //用自定义的类加载器去加载一个类
        Object obj = myLoader.loadClass("java8.classloading.classloader.ClassLoaderTest").newInstance();

        System.out.println(obj.getClass());

        //与相对路径的类相对比
        System.out.println(obj instanceof java8.classloading.classloader.ClassLoaderTest);

    }
}
 运行结果:
  class java8.classloading.classloader.ClassLoaderTest
  false

关于双亲委派模型(Parents Delegation Model)

3种类加载器
  • 启动类加载器(Bootstrap ClassLoader
  1. 这个类加载器使用C++语言实现,是虚拟机自身的一部分;

  2. 无法被Java程序直接引用

  3. 负责把类库加载到虚拟机内存中

    1. 放在<JAVA_HOME>\lib目录中

    2. 或者被 -Xbootclasspath 参数所指定的路径中的

    3. 被虚拟机识别的(按照文件名识别,如rt.jar)

  • 扩展类加载器(Extension ClassLoader)
  1. 由sun.misc.Launcher$ExtClassLoader实现

  2. 开发者可以直接使用

  3. 负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库

  • 应用程序类加载器(Application ClassLoader)
  1. 由sun.misc.Launcher$App-ClassLoader实现

  2. 一般也称为系统加载器。程序中的默认类加载器

  3. 加载用户类路径(ClassPath)上所指定的类库

什么是双亲委派模型

undefined

如图所示:类加载器之间的这种层次关系,称为类加载器的双亲委派模型

  • 工作流程:

如果一个类加载器收到了类加载的请求,先把请求委派给父类加载器去完成,每层如此;只有父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类),子加载器才会尝试自己去加载

  • 好处:

保证程序里各类加载器环境中都是同一个类,对于保证Java程序的稳定运作很重要

(代码集中在java.lang.ClassLoader的loadClass()方法中)

题外

  1. 热部署: 其实是通过自定义类加载机制实现了,其破坏了双亲委派模型(OSGI:Java模块化标准)

  2. 关于反射: 提供很多方法,可以直接通过(类名.方法)的形式获取类的相关信息(变量、方法等)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值