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 类加载是核心逻辑之一,非常重要,对一个变成人员的提升也是非常的有帮助。

  • 相关阅读:
    Python 学习笔记 11.模块(Module)
    Python 学习笔记 8.引用(Reference)
    Python 学习笔记 9.函数(Function)
    Python 学习笔记 6.List和Tuple
    Python 学习笔记 4.if 表达式
    Python 学习笔记 2.自省
    Python 学习笔记 3.简单类型
    Python 学习笔记 7.Dictionary
    Python 学习笔记 5.对象驻留
    Python 学习笔记 10.类(Class)
  • 原文地址:https://www.cnblogs.com/jssj/p/14259220.html
Copyright © 2011-2022 走看看