zoukankan      html  css  js  c++  java
  • 《Java 底层原理》Jvm 类的加载原理

    前言

    一直想好好的了解一下JVM,这次就来一起了解一下JVM是如何实现类的加载过程的。

    原理

    类加载的生命周期

    1. 加载

    5种类加载情况:

    • 在遇到 new、putstatic、getstatic、invokestatic 字节码指令时,如果类尚未初始化,则需要先触发初始化。
    • 对类进行反射调用时,如果类还没有初始化,则需要先触发初始化。
    • 初始化一个类时,如果其父类还没有初始化,则需要先初始化父类。
    • 虚拟机启动时,用于需要指定一个包含 main() 方法的主类,虚拟机会先初始化这个主类。
    • 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类还没初始化,则需要先触发初始化。

    因为Java类加载是不关心这个文件来自哪里?

    所有Java的对象可以来自Jar,war,网络,CGlib,数据库等等。

    使用到对象才会加载。

    2. 验证

    • 文件格式验证
    • 元数据验证
    • 字节码验证
    • 符号引用验证

    3. 准备

    为静态变量分配内存、为基础数据类型赋初值,

    如果被final修饰,在编译的时候会给属性添加ConstantValue属性,准备阶段直接完成赋值操作,无需赋初值。

    4. 解析

    间接引用转直接引用。

    间接引用:指向常量池。

    直接引用:指向内存地址。

    5. 初始化

    执行静态代码段(静态代码块,静态变量):clinit

    测试代码写了一个静态变量b 通过字节码文件也能清楚的看到信息。

    静态代码的执行顺序和定义的顺序一致,改点可以通过案例说明。

    public class JvmTest1 {
    
        public static void main(String[] args) {
            Test1 test1 = Test1.getTest();
            System.out.println(Test1.val1);
            System.out.println(Test1.val2);
        }
    }
    
    class Test1{
        public static int val1 ;
    
        Test1(){
            val1++;
            val2++;
        }
    
        public static Test1 instance = new Test1();
    
        public static int val2 = 1 ;    // 该代码放在在初始化代码
    
        public static Test1 getTest(){
            return instance;
        }
    }

    运行结果:

    运行结果是1和1 ,造成这个结果的原因就是静态代码的顺序执行,先创建了对象,对象中构造函数执行了, 后面再执行 public static int val2 = 1 ,值就被覆盖回去了。

    6. 使用

    对象已经初始化完成,这个对象就可以再其他地方被使用。

    7. 卸载

    销毁之前加载的对象信息。

    Jvm里面有一个类状态的枚举:

    JVM类加载细节

    1. jvm类加载会加锁,防止类加载出现资源争用的情况。

    通过一个案例证明一下:

    public class JvmTest3 {
    
        public static void main(String[] args) {
            new Thread(() -> {
                while(true){
                    new AA();
                }
    
            }).start();
    
            new Thread(() -> {
                while(true) {
                    new BB();
                }
            }).start();
        }
    }
    
    class AA{
        static {
            System.out.println("创建AA对象");
            new BB();
        }
    }
    
    class BB{
        static {
            System.out.println("创建BB对象");
            new AA();
        }
    }

    运行结果:

    执行被锁,无法继续执行。

    2. Jvm加载对象属于懒加载(对象没有使用的时候,不会去加载该对象),下面通过案例说明:

    public class JvmTest2 {
    
        public static void main(String[] args) {
            System.out.println(B.str);
        }
    }
    
    class A {
        static String str = "a";
        static {
            System.out.println("static a");
        }
    }
    
    class B extends A {
        static {
            System.out.println("static b");
        }
    }

    运行结果:

    可以明显的看到B的static方法没有被执行,因为这段逻辑中,不需要是到B这个对象,哪怕B对象是A对象的子类。

    总结

    Jvm 类加载是核心逻辑之一,非常重要,对一个变成人员的提升也是非常的有帮助。

  • 相关阅读:
    【二食堂】Alpha
    【二食堂】Alpha- 发布声明
    【Beta】Scrum Meeting 4
    【Beta】Scrum Meeting 3
    【Beta】Scrum Meeting 2
    【Beta】Scrum Meeting 1
    beta设计和计划
    alpha事后分析
    alpha项目展示
    Scrum Meeting 最终总结
  • 原文地址:https://www.cnblogs.com/jssj/p/14259220.html
Copyright © 2011-2022 走看看