jvm

简述 Java 类及其实例使用历程

2019-04-07  本文已影响44人  Whyn

前言

Java 语言是一门面向对象的语言(Java 类型一切皆对象,基本类型除外),我们使用 Java 语言进行程序编写,都是以类的形式进行组织。各种各样的职能类与工具类配以恰当的业务逻辑构成了一个个功能丰富多彩的应用。

可以认为,使用 Java 语言编写的程序最主要的就是各种类的加载使用。因此,了解 Java 类的加载使用过程,会让我们可以更加高效编写出正确,高效的代码。

因此,本篇博文主要对 Java 类加载及其对象创建的整个过程做一个相对完整的讲解。

Java 类加载及其对象创建过程

熟悉 Java 的人都知道,Java 语言是一门基于 JVM 的平台无关的编程语言。使用 Java 编写的语言最终会经过编译器(即 javac)的编译后,生成 Class 字节码文件。最终由 JVM 将该 Class 文件加载进内存中,创建出一个对应的 Class 对象;然后由该 Class 对象,就可以实现实例的创建。

因此,类的加载与对象创建具体涉及到如下三个过程:

  1. Java 源码编译生成 Class 文件
  2. JVM 加载 Class 文件到内存,生成 Class 对象
  3. 创建类实例

下面依次对上述 3 个过程进行分析

Java 源码编译生成 Class 文件

先简单介绍下 Class 文件:Class 文件是一组以 8 bit(即 1 字节)为基础单位的二进制流,其内的各个数据项都具备一定的描述信息(具体信息参考 Java 虚拟机字节码规范),排列紧凑且无多余内容。

Class 文件存储了 Java 语言定义的类的全部信息,包括类名,类属性和类方法····其具备强大的信息描述能力,而 Class 文件内部结构其实只有两种数据类型:无符号数

类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count-1
u2 access_flag 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attributes_count 1
attribute_info attributes attributes_count

从上述 Class 文件格式表中可以看到,Class 文件其实就是依次存储了以下内容(对 cp_infofield_infomethod_infoattribute_info 这些表的具体格式就不展开讲解了):

Java 虚拟机不予任何语言相关联,包括 Java,它只与 Class 文件有联系。当 Java 源码被编译成 Class 文件后,Java 源码定义的类信息就被完整地存储到 Class 文件中了。

到此,Java 源码经由编译器就会编译成 Class 字节码文件了。

此时,就可以进入到下一步:JVM 加载 Class 文件到内存

JVM 加载 Class 文件到内存,生成 Class 对象

当我们 new 一个对象或者调用了类的静态字段/静态方法时,就会触发类的加载。

在类加载之前,JVM 进程肯定是要先启动,后续才能进行类的加载过程。

JVM 启动时,会把其管理执行 Java 程序的内存划分为若干个不同的数据区域,称为 虚拟机运行时数据区域JVM 内存结构。具体的区域如下:

JVM 内存结构图如下所示:

运行时数据区

当 JVM 内存区域分配完成后,就可以进行类的加载。

类的加载过程主要涉及 3 个阶段操作:加载阶段连接阶段初始化阶段
其中,连接阶段 又可以分为 3 个过程:验证准备解析
如下图所示:

类的生命周期

下面依次对上述类加载过程进行简单讲解:

到此,方法区中就已经存在一个完备的 Class 对象了。

现在,我们就可以 new 出一个实例对象了。

创建类实例

当我们在代码用 new 一个对象时,这个操作反映到 Class 文件上其实就是一个 new 指令,该指令后面会跟随一个类的全限定名。JVM 通过在方法区运行时常量池中,通过该类的全限定名就可以找到对应的类信息,然后就可以在堆中分配一块内存,用于创建该类实例变量,然后依次执行实例的默认初始化,定义初始化和构造初始化,这样,类实例对象就成功创建完成了。

类实例对象创建完后,就可以使用了。当使用结束时,JVM 就会在垃圾回收器启动时,对其进行存活判断,看是否要回收该对象。

因此,接下来就是垃圾回收过程。

垃圾回收

一个对象要想让垃圾收集器进行回收,则首先要判断该对象是否是一个 “无用对象”,即没有其他实例引用该对象。

判断对象是否 “无用” 的方法一般有两种:引用计数法可达性分析

有效引用

假设 A 是一个持久对象(不会被回收),其引用了 B;而 B 引用了 C,C引用了 D,D 由引用了 B。因此,各自对象的引用计数器如上图红色数字所示。假设此时 A 断开了 B 的引用,如下图所示:

无效引用

此时,B,C,D 本来应当算是无效对象,但由于他们循环引用,导致各自的计数器不为0,因此无法回收。

Java 使用的判定算法为:可达性分析法

上面只是对单个对象的状态进行分析,而我们知道,当 GC 时,处理的对象是一大块内存的所有对象,不同的内存中,对象的状态(声明周期)不同,因此,这里就涉及到了垃圾收集算法。

常用的垃圾收集算法有如下 4 种:

到此,对于 Java 类从源码到 JVM 进程的加载及其实例创建使用过程涉及到的一些相关内容,就已分析完毕。

参考

上一篇 下一篇

猜你喜欢

热点阅读