美文网首页
JVM——类加载机制

JVM——类加载机制

作者: Eva_d1e6 | 来源:发表于2021-04-07 16:59 被阅读0次

    一、类加载的过程

    1)载入

    JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的 java.lang.Class 对象。

    2)验证

    这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段是非常重要的,这个阶段是否严谨,直接决定了Java虚拟机是否能承受恶意代码的攻击。

    3)准备

    准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。首先,这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。其次,这里所说的初始值“通常情况”下是数据类型的零值。

    public int value = 123;(这不会被分配内存)

    public static int value = 123;(在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器<clinit>()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行)

    public static final int value = 123;(static final 修饰的变量被称作为常量,和类变量不同。常量一旦赋值就不会改变了,所以value 在准备阶段的值为123而不是 0)

    4)解析

    解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

    符号引用(SymbolicReferences):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用的目标并不一定已经加载到内存中。

    直接引用(DirectReferences):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

    在编译时,Java 类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。直接引用通过对符号引用进行解析,找到引用的实际内存地址。

    5)初始化

    在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。

    二、类加载器

    1)启动类加载器(Bootstrap Class-Loader),加载 jre/lib 包下面的 jar 文件,比如说常见的 rt.jar。

    2)扩展类加载器(Extension or Ext Class-Loader),加载 jre/lib/ext 包下面的 jar 文件。

    3)应用类加载器(Application or App Clas-Loader),根据程序的类路径(classpath)来加载 Java 类。

    双亲委派模型

    工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

    好处:使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。

    public class Test {

        public static void main(String[] args) {

            ClassLoader loader = Test.class.getClassLoader();

            while (loader != null) {

                System.out.println(loader.toString());

                loader = loader.getParent();

            }

        }

    }

    这段代码的输出结果如下:

    sun.misc.Launcher$AppClassLoader@73d16e93

    sun.misc.Launcher$ExtClassLoader@15db9742

    参考:

    《深入理解Java虚拟机:JVM高级特性与最佳实践》

    https://zhuanlan.zhihu.com/p/73078336

    相关文章

      网友评论

          本文标题:JVM——类加载机制

          本文链接:https://www.haomeiwen.com/subject/bvqahltx.html