第七章 类型的生命周期
1、 类型装载、链接与初始化
Java虚拟机通过装载、链接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用。其中,装载就是把二进制的Java类型读入Java虚拟机中,而连接就是把这种已经读入虚拟机的二进制形式的类型数据合并到虚拟机的运行状态中去。连接阶段分了三个步骤:验证、准备和解析。验证确保了Java类型数据格式的正确并且适于Java虚拟机使用,而准备步骤则负责为该类型分配它所需的内存,比如为它的类变量分配内存,“解析”步骤则负责把常量池中的符号引用转换为直接引用(也可以在运行中的程序真正使用某个符号引用时再去解析它)。初始化将给类变量赋予适当的初始值。装载、连接和初始化这三个阶段必须按顺序进行。
虚拟机严格定义了初始化的时机:
① 当创建某个类的新实例时(或者通过在字节码中执行new指令,或者通过不明确的创建、反射、克隆或者反序列化)。
② 当调用某个类的静态方法时(即在字节码中执行invokestatie指令时)。
③ 当使用某个类或接口的静态字段,或者对该字段赋值时(在字节码中,执行getstatic或者putstatic指令时),用final修饰的静态字段除外,它被初始化为一个编译时的常量表达式。
④ 当调用JavaAPI中某些反射方法时,比如类Class中的方法或者java.reflect包中类的方法。
⑤ 当初始化某个类的子类时(某个类初始化时,要求它的超类已经被初始化了)。
⑥ 当虚拟机启动时某个标明为启动类的类(即含main()方法的那个类。)
任何一个类的初始化都要求它的所有祖先类(而不是祖先接口)预先被初始化。而一个接口的初始化,并不要求它的祖先接口预先被初始化。
1) 装载阶段由三个基本动作组成:
① 通过该类型的完全限定名,产生一个代表该类型的二进制数据流。
② 解析这个二进制数据流为方法区内的内部数据结构。
③ 创建一个表示该类型的java.lang.Class类的实例。
如果一个类装载器在预先装载时遇到缺失或者错误的class文件,它必须等到程序首次主动使用该类时才报告错误,如果一个类一直没有被程序主动使用,那么该类装载器就不会报告错误。
2) 验证,类型被转载后,就准备进行连接了,连接过程的第一步是验证,确认类型符合Java语言的语义,并且他不会危及虚拟机的完整性。
3) 准备阶段,Java虚拟机为类变量分配内存,设置默认初始值,虚拟机把给类变量新分配的内存根据类型设置为默认值。准备阶段,Java虚拟机实现可能也为一些数据结构分配内存,目的是提高运行程序的性能。
4) 解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用换成直接引用的过程。
5) 初始化就是为类变量赋予正确的初始值,“正确”初始值指的是程序员希望这个类变量所具备的起始值,正确的初始值是和在准备阶段赋予的默认初始值对比而言的。
初始化一个类包含两个步骤:
① 如果类存在直接超类的话,且直接超类还没有被初始化,就先初始化直接超类。
② 如果类存在一个类初始化方法,就执行此方法。
当初始化一个超类时,也需要包含这两个步骤,因此第一个被初始化的类用远视Object,然后是被主动使用的类的继承树上所有的类,超类总是在子类之前被初始化。初始化接口并不需要初始化它的父接口,因此初始化一个接口只需要一步,如果接口初始化方法的话,就执行此方法。
<clinit>()方法的代码并不显式地调用超类的<clinit>()方法,在Java虚拟机调用类的<clinit>()方法之前,它必须确认超类<clinit>()方法已经被执行了。
Java虚拟机必须确保初始化过程被正确地同步,如果多个线程需要初始化一个类,仅仅允许一个线程初始化,其他线程需要等待,当活动的线程完成了初始化过程后,他必须通知其他等待的线程。
Java编译器把类变量初始化语句和静态初始化语句的代码都放到class文件的<clinit> ()方法中,顺序就按照它们在类或者接口声明中出现的顺序。并非所有的类都需要在它们的class文件中拥有一个<clinit>()方法。如果类声明了类变量,但是没有明确使用类变量初始化语句或者静态初始化语句初始化它们,那么类不会有<clinit> ()方法。如果累仅仅包含静态final变量的类变量初始化语句,而且这些类变量初始化语句采用编译时常量表达式,类也不会有<clinit>()方法,只有那些的确需要执行Java代码来赋予类变量正确初始值的类才会有类初始化方法。
主动使用和被动使用:
2、 对象声明周期
一旦一个类被装载、连接和初始化,它就随时可以使用了。程序可以访问它的静态字段,调用它的静态方法,或者创建它的实例。
1) 类实例化:在Java程序中,类可以被明确或者隐含地实例化。实例化一个类有四种途径:明确地使用new操作符;调用Class或者java.lang.reflect.Constuctor对象的newInstance()方法,调用任何现有对象的clone();或者通用java.io.ObjectInputStream类的getObject()方法反序列化。
当Java虚拟机创建一个类的新实例时,不管是明确的还是隐含的,首先都需要在堆中为保存对象的实例变量分配内存,所有在对象的类中它的超类中声明的变量都要分配内存。
2) 垃圾收集和对象的终结
程序可以明确或者隐含地位对象分配内存,但是不能明确地释放内存。如果类声明了一个名为finalize()的返回void的方法,垃圾收集器会在释放这个实例所占据的内存空间之前执行这个方法(被称为终结方法)一次。垃圾收集器最多只会调用一个对象的终结方法一次(在对象变成不再被引用的之后的某个时候,在占据的对象被重用之前)。
3、 卸载类型
在很多方面,Java虚拟机中类的生命周期和对象的生命周期很相似。类的垃圾收集和卸载之所以在java虚拟机中很重要,因为java程序可以在运行时通过用户自定义的类装载器装载类型来动态的扩展程序。所有被装载的类型都在方法区占据内存空间,如果Java程序持续通过用户自定义的类装载器装载类型,方法区的内存足迹就会不断增长,如果某些动态装载的类型只是临时需要,当它们不再被引用之后,占据的内存空间可以通过卸载类型而释放。
Java虚拟机通过何种方法来确定一个动态装载类型是否仍然需要被程序需要呢?其判断方式与判断对象是否仍然被程序需要的方式很类似。如果程序不再引用某类型,那么这个类型就无法再对未来的计算过程产生影响。类型变成不可触及的,而且可以被垃圾收集。
使用启动类装载器装载的类型永远是可触及的,所以永远不会被卸载,只有使用用户自定义类装载器装载的类型才会变成不可触及的。
判断动态装载的类型Class实例在正常的垃圾收集过程中是否可以触及有两种方式,第一,也是最明显的,如果程序保持对Class实例的明确引用,它就是可触及的。其次,如果在堆中还存在一个触及的对象,在方法区中它的类型数据指向一个Class实例,那么这个Class实例就是可触及的。
分享到:
相关推荐
虚拟机栈的生命周期与线程相同,每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。 堆区是 JVM 的最大的一块内存区域,是被线程共享的区域,在虚拟机启动时创建。所有类的实例...
虚拟机学习笔记 Java 虚拟机(JVM)是 Java 语言的 runtime 环境,负责加载、验证、执行 Java 字节码。以下是 JVM 相关知识点的总结。 1. 运行时数据区域 JVM 的运行时数据区域主要包括: * 堆(Heap):...
### 深入Java虚拟机JVM类加载学习笔记 #### 一、Classloader机制解析 在Java虚拟机(JVM)中,类加载器(ClassLoader)是负责将类的`.class`文件加载到内存中的重要组件。理解类加载器的工作原理对于深入掌握JVM以及...
### 学习深入理解Java虚拟机的前几章笔记 #### JVM内存模型 Java虚拟机(JVM)的内存模型主要分为两大类:线程共享区和线程私有区。 ##### 线程共享区 - **堆**:是所有线程共享的内存区域,在这里存放着对象实例...
本篇文章将深入探讨Java虚拟机中的垃圾收集器(GC)及其对内存管理的影响。 1. 垃圾收集器的由来与作用 垃圾收集器的引入主要是为了解决内存管理的问题。在没有GC的情况下,程序员需要手动释放不再使用的对象,这...
2. **类与对象**:深入解析类的定义、对象的创建及生命周期,包括封装、继承和多态等面向对象的三大特性,以及构造函数、访问修饰符的使用。 3. **包与接口**:介绍了Java中的包机制,用于组织和管理类,以及接口的...
- Java的设计目标是“一次编写,到处运行”,通过Java虚拟机(JVM)实现跨平台性。 2. **Java环境搭建**: - 安装Java Development Kit (JDK),包含Java编译器javac和运行时环境Java Runtime Environment (JRE)。...
此外,Java 5.0还引入了"Annotations(注解)",这是一种元数据,可以为编译器和Java虚拟机提供额外的信息,用于代码分析、编译时检查或运行时行为的改变。"枚举(Enums)"是另一种新的数据类型,用于创建不可变的、...
- 对象生命周期:创建、使用、垃圾收集。Java自动管理内存,程序员无需手动释放内存。 - 垃圾收集器:当一个对象不再被引用时,垃圾收集器会回收其占用的内存。 6. **集合框架** - List、Set、Map接口:分别代表...
### Java分布式应用学习笔记02再谈JVM 在深入探讨Java虚拟机(JVM)时,我们再次聚焦于这个核心组件,它不仅是Java运行环境的心脏,也是构建分布式应用的关键技术之一。JVM作为Java语言的核心执行环境,其设计与...
### Java学习笔记知识点总结 #### 一、JVM与内存管理 ...通过以上知识点的总结,我们可以清晰地了解到Java学习笔记中涵盖的主要内容和技术细节,有助于深入理解和掌握Java语言及相关的开发技术。
**线程的生命周期:** 线程的生命周期包括新建、就绪、运行、阻塞和死亡等状态。 **Thread的方法:** Thread类提供了start()、run()、join()等方法来控制线程的执行。 **共享数据的并发处理:** 并发处理共享...
- 局部变量:定义在方法或语句块内的变量,生命周期与该方法或语句块相同。 - 成员变量:定义在类内部、方法外部的变量,与对象关联。 - 静态变量:定义时使用`static`关键字,与类关联。 8. **常量**: - 初始...
基本数据类型在栈中分配内存,生命周期由Java自动管理。引用数据类型(如类、接口、数组)在栈和堆中分配,栈中存放的是指向堆中对象的引用。对于String,直接赋值(如"abc")会创建字符串常量池中的引用,而使用new...
### Java实战经典学习笔记知识点概览 #### 一、Java概述及开发环境搭建 - **Java概述** - Java是一种广泛使用的高级编程语言,由Sun Microsystems于1995年发布。 - Java的设计目标是“一次编写,到处运行”,这...
最后,笔记可能会提及Java的异常处理、反射机制、注解(Annotation)以及JVM(Java虚拟机)的工作原理,这些都是深入理解Java的必备知识。 总的来说,“java基础知识学习笔记”是一份全面的教程,覆盖了Java编程的...
在讨论Java编程思想学习笔记时,首先需要了解的是Java语言的平台无关性,而这一特性正是通过Java虚拟机(JVM)得以实现的。JVM作为Java程序设计的关键组成部分,对于Java开发人员来说是必须掌握的基础知识。在该学习...
- **生命周期**:新建、就绪、运行、阻塞、死亡。 - **线程方法**:如`start()`、`run()`、`join()`等。 - **并发处理**:多个线程访问共享资源时需要注意同步问题。 - **互斥锁**:使用`synchronized`关键字或其他...
### Java入门学习笔记 #### 一、Java特点与运行原理 **1.1 Java特点** - **简单性:** Java的设计使得它易于学习且避免了许多传统编程语言中存在的复杂性。 - **面向对象:** Java是一种纯面向对象的语言,支持...