文章目录
- JAVA对象的生存周期
- 1.创建阶段(Created)
- 为对象分配内存需要考虑的问题
- 2.应用阶段(In Use)
- 3.不可见阶段(Invisible)
- 4.不可达阶段(Unreachable)
- 5.收集阶段(Collected)
- 6.终结阶段
- 7.对象空间重新分配阶段
JAVA对象的生存周期
在Java中,对象的生命周期包括以下几个阶段:
创建阶段(Created)应用阶段(In Use)不可见阶段(Invisible)不可达阶段(Unreachable)收集阶段(Collected)终结阶段(Finalized)对象空间重分配阶段(De-allocated)
1.创建阶段(Created)
- 在创建阶段系统通过下面的几个步骤来完成对象的创建过程
- 虚拟机遇到new指令,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用。
- 检查符号引用的类是否引进被加载,如果没有被加载,则执行相应的类加载过程。
- 为对象分配内存(为对象分配的内存大小在类加载时已经确定)(为对象分配内存还有一点知识点我在后面赘述)
- 开始构造对象
- 从超类到子类对static成员进行初始化
- 超类成员变量按顺序初始化,递归调用超类的构造方法
- 子类成员变量按顺序初始化,子类构造方法调用
- 一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段
Java对象的创建这里涉及到知识点储备需要知道,下面这些基本的知识,下面点击链接可以自行查阅。
JVM内存区域划分JVM类生命周期概述:加载时机与加载过程
为对象分配内存需要考虑的问题
- 问题一:堆内存的分配方式
为对象分配内存时,对象需要的内存大小在类加载时就已经确定,这里为对象分配空间的任务相当于把一块确定大小的内存从堆中划分出来。而堆内存的分配方式是我们这里要说的第一个问题:
- 分配方式一:指针碰撞
假设JVM中堆的内存是绝对规整的,空闲的内存在一遍,已经被使用的内存在一遍,中间由一个指针进行分割。那么对象的内存分配就由将指针移动即可。 - 分配方式二:空闲列表法
如果JVM中堆的内存不是规整的,空闲的内存和被使用的内存相互交错。这时虚拟机需要维护一个列表,记录哪块内存时空闲的,当对象需要被分配内存时,找到一块足够大的内存分配。
- **考虑分配方式取决于JVM堆内存是否规整,而JVM内存是否规整取决于JVM使用的垃圾回收器是否带有垃圾整理功能。**例如:在使用Serial、ParNew 等带Compact过程的收集器时,系统采用的分配算法是指针碰撞;而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。
- 问题二:考虑对象是否被频繁创建,线程安全问题
即使是修改一个指正,但是在并发的情况下也不是安全的。可能存在对象A正在分配的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。解决这个问题有两种方案:
- 第一种:是对分配内存空间的动作进行同步处理,实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性:
- 第二种:是把内存分配的动作按照线程划分在不同的空间之中进行,:即每个线程在Java堆中预先分配小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)。 哪个线程要分配内存,就在哪个线程的TLAB上分配,只有TLAB用完并分配新的FLAB时,才需要同步锁定。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头), 如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对
应的零值。虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。
2.应用阶段(In Use)
对象至少被一个强引用持有着。
想要了解引用的朋友可以看博文
Java中的四种引用
3.不可见阶段(Invisible)
当一个对象处于不可见阶段时,说明程序本身不再持有该对象的任何强引用,虽然该这些引用仍然是存在着的。
简单说就是程序的执行已经超出了该对象的作用域了。
举例如下图:本地变量count在25行时已经超出了其作用域,则在此时称之为count处于不可视阶段。当然这种情况编译器在编译的过程中会直接报错了。
4.不可达阶段(Unreachable)
对象处于不可达阶段是指该对象不再被任何强引用所持有。
与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用。对象如果在之前的任何状态被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收,则对方也无法到达“不可达阶段”。
5.收集阶段(Collected)
当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。如果该对象已经重写了finalize()方法,则会去执行该方法的终端操作。
这里要特别说明一下:不要重载finazlie()方法!原因有两点:
l 会影响JVM的对象分配与回收速度
在分配该对象时,JVM需要在垃圾回收器上注册该对象,以便在回收时能够执行该重载方法;在该方法的执行时需要消耗CPU时间且在执行完该方法后才会重新执行回收操作,即至少需要垃圾回收器对该对象执行两次GC。
l 可能造成该对象的再次“复活”
在finalize()方法中,如果有其它的强引用再次持有该对象,则会导致对象的状态由“收集阶段”又重新变为“应用阶段”。这个已经破坏了Java对象的生命周期进程,且“复活”的对象不利用后续的代码管理。
想要了解JVM垃圾回收算法可以点击链接
JVM垃圾回收算法
6.终结阶段
当对象执行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。
7.对象空间重新分配阶段
垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”。