Java 对象的创建和生命周期
Java 中创建对象的 5 种方式
使用 new 关键字 → 调用了构造函数
Employee emp1 = new Employee();
使用 Class 类的 newInstance 方法→ 调用了构造函数
<!--使用 Class 类的 newInstance 方法创建对象。这个 newInstance 方法调用无参的构造函数创建对象。-->
Employee emp2 = (Employee) Class.forName("org.programming.mitra.exercises.Employee").newInstance();
使用 Constructor 类的 newInstance 方法 → 调用了构造函数
<!--和 Class 类的 newInstance 方法很像, java.lang.reflect.Constructor 类里也有一个 newInstance 方法可以创建对象-->
Constructor<Employee> constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();
使用 clone 方法→ 没有调用构造函数
<!--无论何时我们调用一个对象的 clone 方法,jvm 就会创建一个新的对象,将前面对象的内容全部拷贝进去。用 clone 方法创建对象并不会调用任何构造函数。-->
<!--要使用 clone 方法,我们需要先实现 Cloneable 接口并实现其定义的 clone 方法-->
Employee emp4 = (Employee) emp3.clone();
使用反序列化→ 没有调用构造函数
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject();
Java 对象生命周期
对象的整个生命周期大致可以分为 7 个阶段:
创建阶段(Creation)
在创建阶段系统通过下面的几个步骤来完成对象的创建过程
1,为对象分配存储空间
2,开始构造对象
3,从超类到子类对 static 成员进行初始化
4,超类成员变量按顺序初始化,递归调用超类的构造方法
5,子类成员变量按顺序初始化,子类构造方法调用
一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段
应用阶段(Using)
对象至少被一个强引用持有着
不可视阶段(Invisible)
当一个对象处于不可见阶段时,说明程序本身不再持有该对象的任何强引用,虽然该这些引用仍然是存在着的。
简单说就是程序的执行已经超出了该对象的作用域了。
不可到达阶段(Unreachable)
对象处于不可达阶段是指该对象不再被任何强引用所持有
与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用,这种情况下,该对象仍可能被 JVM 等系统下的某些已装载的静态变量或线程或 JNI 等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些 GC root 会导致对象的内存泄露情况,无法被回收。
可收集阶段(Collected)
当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。
如果该对象已经重写了 finalize() 方法,则会去执行该方法的终端操作。
终结阶段(Finalized)
当对象执行完 finalize() 方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。
对象空间重新分配阶段
垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”。
类的生命周期
jvm(java 虚拟机)中的几个比较重要的内存区域,这几个区域在 java 类的生命周期中扮演着比较重要的角色:
- 方法区:在 java 的虚拟机中有一块专门用来存放已经加载的类信息、常量、静态变量以及方法代码的内存区域,叫做方法区。
- 常量池:常量池是方法区的一部分,主要用来存放常量和类中的符号引用等信息。
- 堆区:用于存放类的对象实例。
- 栈区:也叫 java 虚拟机栈,是由一个一个的栈帧组成的后进先出的栈式结构,栈桢中存放方法运行时产生的局部变量、方法出口等信息。当调用一个方 法时,虚拟机栈中就会创建一个栈帧存放这些数据,当方法调用完成时,栈帧消失,如果方法中调用了其他方法,则继续在栈顶创建新的栈桢。
当我们编写一个 java 的源文件后,经过编译会生成一个后缀名为 class 的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在 java 虚拟机中运行,java 类的生命周期就是指一个 class 文件从加载到卸载的全过程
一个 java 类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直接被使用的情况
在加载阶段
- 找到需要加载的类并把类的信息加载到 jvm 的方法区中,然后在堆区中实例化一个 java.lang.Class 对象,作为方法区中这个类的信息的入口。
- 类的加载方式比较灵活,我们最常用的加载方式有两种,一种是根据类的全路径名找到相应的 class 文件,然后从 class 文件中读取文件内容;另一种是从 jar 文件中读取。
- 对于加载的时机,各个虚拟机的做法并不一样,但是有一个原则,就是当 jvm“预期”到一个类将要被使用时,就会在使用它之前对这个类进行加载。比 如说,在一段代码中出现了一个类的名字,jvm 在执行这段代码之前并不能确定这个类是否会被使用到,于是,有些 jvm 会在执行前就加载这个类,而有些则在 真正需要用的时候才会去加载它,这取决于具体的 jvm 实现。我们常用的 hotspot 虚拟机是采用的后者,就是说当真正用到一个类的时候才对它进行加载。
- 加载阶段是类的生命周期中的第一个阶段,加载阶段之后,是连接阶段。有一点需要注意,就是有时连接阶段并不会等加载阶段完全完成之后才开始,而是 交叉进行,可能一个类只加载了一部分之后,连接阶段就已经开始了。但是这两个阶段总的开始时间和完成时间总是固定的:加载阶段总是在连接阶段之前开始,连 接阶段总是在加载阶段完成之后完成。
连接
- 连接阶段比较复杂,一般会跟加载阶段和初始化阶段交叉进行,这个阶段的主要任务就是做一些加载后的验证工作以及一些初始化前的准备工作,可以细分为三个步骤:验证、准备和解析。
- 验证:当一个类被加载之后,必须要验证一下这个类是否合法,比如这个类是不是符合字节码的格式、变量与方法是不是有重复、数据类型是不是有效、继承与实现是否合乎标准等等。总之,这个阶段的目的就是保证加载的类是能够被 jvm 所运行
- 准备:准备阶段的工作就是为类的静态变量分配内存并设为 jvm 默认的初值,对于非静态的变量,则不会为它们分配内存。
- 解析:这一阶段的任务就是把常量池中的符号引用转换为直接引用
- 比如我们要在内存中找一个类里面的一个叫做 show 的方法,显然是找不到。但是在解析阶段,jvm 就会把 show 这个名字转换为指向方法区的的一 块内存地址,比如 c17164,通过 c17164 就可以找到 show 这个方法具体分配在内存的哪一个区域了。这里 show 就是符号引用,而 c17164 就 是直接引用。在解析阶段,jvm 会将所有的类或接口名、字段名、方法名转换为具体的内存地址。
- 连接阶段完成之后会根据使用的情况(主动引用还是被动引用)来选择是否对类进行初始化。
初始化
- 如果一个类被主动引用,就会触发类的初始化。
- 在 java 中,直接引用的情况有,通过 new 关键字实例化对象、读取或设置类的静态变量、调用类的静态方法。通过反射方式执行以上三种行为。初始 化子类的时候,会触发父类的初始化。作为程序入口直接运行时(也就是直接调用 main 方法)。除了以上四种情况,其他使用类的方式叫做被动引用,而被动引 用不会触发类的初始化
使用
- 类的使用包括主动引用和被动引用
- 被动引用:引用父类的静态字段,只会引起父类的初始化,而不会引起子类的初始化。定义类数组,不会引起类的初始化。引用类的常量,不会引起类的初始化。
卸载
- 满足下面的情况,类就会被卸载:该类所有的实例都已经被回收,也就是 java 堆中不存在该类的任何实例。加载该类的 ClassLoader 已经被回收。该类对应的 java.lang.Class 对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
- 如果以上三个条件全部满足,jvm 就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java 类的整个生命周期就结束了。
总结
- 对象基本上都是在 jvm 的堆区中创建,在创建对象之前,会触发类加载(加载、连接、初始化),当类初始化完成后,根据类信息在堆区中实例化类对象,初始化非静态变量、非静态代码以及默认构造方法,当对象使用完之后会在合适的时候被 jvm 垃圾收集器回收。
- 对象的生命周期只是类的生命周期中使用阶段的主动引用的一种情况(即实例化类对象)。而类的整个生命周期则要比对象的生命周期长的多。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: 深入理解 JVM
下一篇: 机器学习 和 深度学习
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论