前言
在 java 编程里,对象的创建和销毁是基础且关键的操作,深刻理解这一过程有助于编写出高效、稳定的代码。下面将详细阐述 java 中对象的创建和销毁过程。
对象的创建过程
1. 类加载检查
当代码中使用 new
关键字创建对象时,java 虚拟机(jvm)首先会检查该对象对应的类是否已经被加载到内存中。如果尚未加载,jvm 会通过类加载器将该类的字节码文件加载到内存,并对其进行验证、准备和解析等操作,最终完成类的初始化。例如,当执行 person person = new person();
时,jvm 会先确认 person
类是否已加载。
2. 分配内存
类加载完成后,jvm 会为新对象分配内存空间。分配内存的方式主要有两种:
- 指针碰撞:假设 java 堆中的内存是规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”(bump the pointer)。
- 空闲列表:如果 java 堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(free list)。
选择哪种分配方式由 java 堆是否规整决定,而 java 堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理(compact)的能力决定。
3. 初始化零值
内存分配完成后,jvm 会将分配到的内存空间都初始化为零值(不包括对象头)。这一步操作保证了对象的实例字段在 java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。例如,int
类型的字段初始值为 0,boolean
类型的字段初始值为 false
。
4. 设置对象头
jvm 会对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 gc 分代年龄等信息。这些信息存放在对象的对象头(object header)之中。
5. 执行 init
方法
在上述工作都完成之后,从 java 程序的视角看来,对象已经产生了,但从 jvm 的视角来看,对象创建才刚刚开始——<init>
方法还没有执行,所有的字段都还为零。所以一般来说(由字节码中是否跟随有 invokespecial
指令所决定),执行 new
指令之后会接着执行 <init>
方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全被构造出来。
对象的销毁过程
1. 可达性分析
在 java 中,对象的销毁主要由垃圾回收机制(gc)负责。jvm 会通过可达性分析算法来判断对象是否存活。该算法以一系列被称为“gc roots”的对象为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到 gc roots 没有任何引用链相连时,则证明此对象是不可达的,就有可能被回收。常见的 gc roots 包括:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中 jni(即一般说的 native 方法)引用的对象。
2. 第一次标记
当对象被判定为不可达时,它会被第一次标记。但此时对象还不会被立即回收,而是会被放入一个名为 f - queue
的队列中,并由一个低优先级的线程去执行队列中对象的 finalize()
方法(如果对象重写了该方法)。
3. finalize() 方法执行
finalize()
方法是对象逃脱死亡命运的最后一次机会。在该方法中,对象可以重新与引用链上的任何一个对象建立关联,例如把自己(this
关键字)赋值给某个类变量或者对象的成员变量。如果对象在 finalize()
方法中成功拯救了自己,那在第二次标记时它将被移除出“即将回收”的集合;如果对象没有逃脱,那基本上它就真的要被回收了。不过需要注意的是,finalize()
方法的执行是不可靠的,jvm 并不保证该方法一定会被执行。
4. 第二次标记
如果对象在 finalize()
方法执行后仍然没有与 gc roots 建立引用关系,它会被进行第二次标记。经过第二次标记的对象,就会被真正地列入可回收对象的集合。
5. 垃圾回收
当垃圾回收器执行垃圾回收操作时,会回收那些经过第二次标记的对象所占用的内存空间,将其释放回 java 堆中,供后续新对象的分配使用。不同的垃圾回收器采用不同的算法和策略来执行垃圾回收,例如标记 - 清除算法、标记 - 整理算法、复制算法等。
知识补充:
在 java 虚拟机的堆区,每个对象都可能处于以下三种状态之一。
1)可触及状态:当一个对象被创建后,只要程序中还有引用变量引用它,那么它就始终处于可触及状态。
2)可复活状态:当程序不再有任何引用变量引用该对象时,该对象就进入可复活状态。在这个状态下,垃圾回收器会准备释放它所占用的内存,在释放之前,会调用它及其他处于可复活状态的对象的 finalize() 方法,这些 finalize() 方法有可能使该对象重新转到可触及状态。
3)不可触及状态:当 java 虚拟机执行完所有可复活对象的 finalize() 方法后,如果这些方法都没有使该对象转到可触及状态,垃圾回收器才会真正回收它占用的内存。
总结
到此这篇关于java中对象的创建和销毁过程的文章就介绍到这了,更多相关java对象的创建和销毁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论