• 类加载的时机


    类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。

    其中验证、准备、解析3个部分统称为连接。加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的。

    有且只有五种情况必须立即对类进行“初始化”:

    1. 遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
    2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
    3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
    4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类。
    5. 当使用JDK1.7的动态语言支持时,如果一个java.lang.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

    这五种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。

    被动引用例子之一:

    public class SuperClass {
    
        static {
            System.out.println("SuperClass init!");
        }
    
        public static int value = 123;
    
    }
    public class SubClass extends SuperClass {
    
        static {
            System.out.println("SubClass init!");
        }
    
    }
    public class NotInitialization {
    
        public static void main(String[] args) {
            System.out.println(SubClass.value);
        }
    }

    运行之后只会输出“SuperClass init!”,而不会输出“SubClass init!”。对于静态字段,只有直接定义这个字段的类才会被初始化

    被动引用例子之二:

    public class NotInitialization {
    
        public static void main(String[] args) {
            SuperClass[] sc = new SuperClass[10];
        }
    }

    没有输出“SuperClass init!”。通过数组定义来引用类,不会触发此类的初始化。

    被动引用例子之三(常量传播优化):

    public class ConstClass {
    
        static{
            System.out.println("ConstClass init!");
        }
    
        public static final String HELLOWORLD = "hello world";
    
        public static void test(){
            System.out.println("test.");
        }
    
    }
    public class NotInitialization {
    
        public static void main(String[] args) {
            System.out.println(ConstClass.HELLOWORLD);
        }
    }
    

    控制台输出:

    hello world

    javap 查看编译后的NotInitialization.class

    public class com.fcs.jvms.NotInitialization
      SourceFile: "NotInitialization.java"
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #6.#20         //  java/lang/Object."<init>":()V
       #2 = Fieldref           #21.#22        //  java/lang/System.out:Ljava/io/PrintStream;
       #3 = String             #23            //  hello world
       #4 = Methodref          #24.#25        //  java/io/PrintStream.println:(Ljava/lang/String;)V
       #5 = Class              #26            //  com/fcs/jvms/NotInitialization
       #6 = Class              #27            //  java/lang/Object
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcom/fcs/jvms/NotInitialization;
      #14 = Utf8               main
      #15 = Utf8               ([Ljava/lang/String;)V
      #16 = Utf8               args
      #17 = Utf8               [Ljava/lang/String;
      #18 = Utf8               SourceFile
      #19 = Utf8               NotInitialization.java
      #20 = NameAndType        #7:#8          //  "<init>":()V
      #21 = Class              #28            //  java/lang/System
      #22 = NameAndType        #29:#30        //  out:Ljava/io/PrintStream;
      #23 = Utf8               hello world
      #24 = Class              #31            //  java/io/PrintStream
      #25 = NameAndType        #32:#33        //  println:(Ljava/lang/String;)V
      #26 = Utf8               com/fcs/jvms/NotInitialization
      #27 = Utf8               java/lang/Object
      #28 = Utf8               java/lang/System
      #29 = Utf8               out
      #30 = Utf8               Ljava/io/PrintStream;
      #31 = Utf8               java/io/PrintStream
      #32 = Utf8               println
      #33 = Utf8               (Ljava/lang/String;)V
      ...

    发现常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用定义常量的类,因此不会触发定义常量的类的初始化。

    ================================== 赵客缦胡缨,吴钩霜雪明。 银鞍照白马,飒沓如流星。 ==================================
  • 相关阅读:
    SQL_TRACE与tkprof分析
    mysql学习之-三种安装方式与版本介绍
    1400
    输出二叉树中所有从根结点到叶子结点的路径
    [置顶] 处世悬镜之舍之
    Azkaban2配置过程
    [置顶] 处世悬镜之舍之 二
    UVALIVE 5893 计算几何+搜索
    Paxos算法 Paxos Made Simple
    Spring AOP 详解
  • 原文地址:https://www.cnblogs.com/lucare/p/9312653.html
Copyright © 2020-2023  润新知