理解java的类生命周期与加载过程

  • 什么是类加载机制
    java文件经过JAVAC编译成class的字节码文件存储在磁盘中。JVM从磁盘中将class文件加载到内存,并对加载的该class文件进行校验,连接,初始化后,形成可以被JVM解析和编译的文件的过程,叫做类加载机制。
  • java的类加载过程,校验、连接、初始化是在程序运行时期间完成的,为java提供极高的灵活性和扩展性。

类生命周期

在这里插入图片描述
类生命周期中
加载、校验、准备、初始化、卸载这五个阶段的执行顺序是确定的,类的加载过程中,必须按找这个五个步骤按部就班的开始。
解析阶段则不一定,可以在初始化阶段之后再进行,这是因为java的动态绑定特性。
《java虚拟机规范》中并没有强制规定在什么情况下开始第一阶段“加载”的执行,这可以交给虚拟机的具体实现来自由把握。但是严格规定了六种情况之下,必须对类进行“初始化”操作。当然,在进行初始化操作之前,加载、校验、准备会在初始化之前开始。

必须对类进行初始化的六中情况

  1. 以下场景如果没有进行初始化,则需要先触发其初始化

    • 使用new关键字实例化对象的时候
    • 读取或者设置一个静态类的字段(被final修饰、已在编译时期就把结果放入常量池的静态字段除外)
    • 调用一个类的静态方法时
  2. 使用java.lang.reflect包对类进行反射调用的时候,如果没有对该类进行初始化,则需要先触发其初始化。

  3. 当初始化一个类的时候,如果没有对父类进行初始化,则需要先对其父类进行初始化。

  4. 当虚拟机启动时,用户要先指定一个执行的主类(main()方法的类),需要先对该主类进行初始化。

  5. 当使用JDK 7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解
    析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句
    柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。

  6. 当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有
    这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

类加载过程

  1. 加载(Loading)

     	将磁盘中由javac编译成的class文件加载到内存中。 
    

    加载到内存中的class会对应的在内存中开辟两块内存:

    • 一块是将这个class文件加载到内存后存放的区域(JVM将student.class由磁盘加载到内存,是不是得开辟一块空间来存放这个student.class文件呢)
    • 另一块是生成的代表这个类的java.lang.Class对象,并且这个Class对象会指向存储在内存中的那块区域,作为方法区这个类的访问入口。

在这里插入图片描述
Jvm将student.class文件加载到内存中,会开启一块内存用来存放这个student.class,另外还会开辟一块内存用来生成这个Student类的java.lang.Class对象,并且指向用来存储的那块内存。School对象或者Score对象需要访问Student对象的数据,只能通过访问Student的class对象来获取。

  1. 链接(Linking)

    1. 校验(Verification)

       验证从磁盘加载的class文件是否是一个合格的符合class规范的文件。  
       保证这些加载进来的class运行后不会危害jvm本身。
      

      验证的阶段:

      • 文件格式验证

        1.是否以魔数0xCAFEBABE开头;  
        2.主次版本号是否在JVM的接受范围内(低版本的JVM不能运行高版本的java文件)  
        ...
        
      • 元数据验证

        这个阶段是对字节码描述的语义是否符合《java语言规范》里面的要求  
        1.该类是否具有父类(除了java.lang.Object外,所有的类都应该具有父类)  
        2.该类是否继承了不能被允许继承的父类(final修饰的父类不能被继承)  
        3.是否实现了需要被实现的父类方法。  
        ...
        
      • 字节码验证

        第三阶段是整个验证过程中最繁杂的一个阶段。  
        主要是验证程序的语义是合法的、符合逻辑的。  
        1.确保任意时刻操作数栈的数据类型与指令代码序列都能配合工作:  
        在操作栈放了一个int类型的数据,却按照long类型来加载入本地变量。  
        2.确保方法体中的类型转换是有效的:把一个子类对象赋值给父类,这是安全的。但是  
          如果将一个父类对象赋值给子类,这就是不合法的。  
        ...
        
      • 符号引用验证

        最后一个阶段的校验行为发生在虚拟机将符号引用转化为直接引用 的时候,  
        这个转化动作将在连接的第三阶段————解析阶段中发生。  
        符号引用验证可以看作是对类自身以外(常量池中的各种符号引用)的各类信息进行匹配性校验,  
        通俗来说就是,该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。
        
    2. 准备(Preparation)

      这一阶段为类变量分配内存以及赋初值
      
      • 类变量:是指被static修饰的变量。
      • 赋初值:是指初始值,而不是程序设置的值。比如:publc static int aa = 9;在这一阶段aa的初始值是0而不是9,将9赋值给aa需要到初始化阶段才会被执行。
      • 这一阶段仅对类变量进行内存分配,而不包括实例变量,实例变量会随对象实例化时一同分配到java堆中。
      • 如果类变量被final修饰,初值为程序设置的值。如:publc static final int bb = 99;这时这一阶段后bb的值为99。
    3. 解析(Resolution)

      解析阶段是将Jvm将常量池内的符号引用转化为直接引用的过程
      
      • 符号引用
        以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时无歧义地能够定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标不一定是加载到虚拟机内存当中的内容。

        emm…有点抽象

        • 符号引用常以CONSTANT_Methodref_info、CONSTANT_Class_info、CONSTANT_Utf8_info等等类型的常量出现,每种类型都有自己特定的意义,比如:“CONSTANT_Utf8_info”代表UTF-8的字符串,而“CONSTANT_Methodref_info”代表类中的方法。JVM中有17中常量类型,每种都有自己的特定含义,是比较负责的,这里不具体展示。

        • 符号引用属于编译原理方面的概念。jvm在加载class文件的时候是进行动态链接的,也就是说在class中不会保存各个方法、字段在内存中的布局信息(也就是说并不知道引用的实际地址),因为各种虚拟机实现的内存布局信息可能各不相同,但是接收到的符号引用是一致的,因为符号引用的字面量形式明确定义在java虚拟机规范的class文件格式中。比如:Student类中有个getName方法,在编译时使用“CONSTANT_Methodref_info”来描述这个getName方法。

      • 直接引用
        直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。

        直接引用可能是:

        • 直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
        • 相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
        • 一个能间接定位到目标的句柄
  2. 初始化(Initialization)

     类的初始化阶段是类加载过程的最后一步,  
     这个阶段java虚拟机才真正开始执行类中编写的java程序代码。  
     在准备阶段,已经对类变量进行了内存分配与赋默认值。  
     而初始化阶段会将程序代码设置的值赋给对应的类变量。
    

    比如:public static int aa = 99; 在准备阶段aa = 0;在初始化阶段aa = 99;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java生命周期包括加载、验证、准备、解析、初始化和使用几个阶段。加载阶段指的是将的class文件读入内存,并在内存中为之创建一个Class对象。验证阶段是检查class文件的正确性。准备阶段是为的静态变量分配内存空间,并将其初始化为默认值。解析阶段是将中的符号引用转化为直接引用。初始化阶段是为的静态变量赋予正确的初始值。使用阶段是指及其成员变量、方法可以被应用程序使用。 ### 回答2: Java生命周期指的是从加载、连接、初始化、使用到卸载的整个过程。下面将详细说明Java生命周期。 首先是加载阶段,当程序中需要使用某个时,Java虚拟机会根据的名称找到对应的class文件,并将其加载到内存中。加载阶段包括三个步骤:加载、验证和准备。 加载阶段:虚拟机读取class文件的二进制数据,并将其放入方法区内存中,形成的Class对象。 验证阶段:对的二进制数据进行合法性验证,确保满足Java虚拟机的要求,例如检查文件格式、语义验证等。 准备阶段:为的静态变量分配内存空间,并设置初始值。 接下来是的连接阶段,连接阶段包括三个步骤:解析、初始化和完成。 解析阶段:将中的符号引用转换为直接引用,解析过程通常是在加载的同时完成。 初始化阶段:执行构造器<clinit>()方法,对静态变量进行初始化。在该阶段,Java虚拟机会按照的初始化顺序依次初始化静态变量。 完成阶段:表示的连接阶段已经完成,此阶段仅为标记状态并没有具体的操作。 最后是的使用阶段,当加载和连接完成后,就可以对进行实例化、访问的静态变量和调用的静态方法。 最后是的卸载阶段,在特定条件下,Java虚拟机会卸载,释放内存空间。当的实例已经被GC判定为不可达时,虚拟机会将其卸载。 总结:Java生命周期包括加载、连接、初始化、使用和卸载五个阶段,每个阶段都有具体的操作和规则。了解生命周期有助于我们理解Java类加载机制以及代码的执行过程。 ### 回答3: Java生命周期主要包括加载验证、准备、解析、初始化、对象实例化和对象销毁。 1. 加载:当程序中使用到某个时,Java虚拟机通过加载加载的字节码文件,将其转化为对应的Class对象。加载器根据的全限定名在文件系统、网络或其他地方找到对应的字节码文件,并将其加载到内存中。 2. 验证:在加载完成后,Java虚拟机对进行验证,确保其字节码文件合法、安全,没有被篡改。 3. 准备:在验证通过后,系统为变量分配内存空间,并设置默认初始值。 4. 解析:解析中的符号引用,将其替换为直接引用,以便能够快速访问到目标。例如,将在字节码中出现的符号引用转换为内存地址。 5. 初始化:在被首次主动调用时,虚拟机会对进行初始化。初始化阶段主要完成变量的赋值和静态代码块的执行。 6. 对象实例化:当初始化完成后,可以通过new关键字创建的对象实例。在对象实例化时,会为对象分配内存空间,并调用构造方法对对象进行初始化。 7. 对象销毁:当对象不再被使用时,Java的垃圾回收机制会自动回收该对象所占用的内存空间。垃圾回收机制通过判断对象是否可达来确定对象是否需要回收。 总之,Java生命周期是从加载开始,到对象销毁结束。通过加载、验证、准备、解析、初始化等阶段,确保的正确加载和初始化。同时,通过对象实例化和垃圾回收机制,管理对象的生命周期,提高系统的性能并保证内存的有效使用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值