首先需要确认的一点为类首先需要进行初始化,后进行实例化。这是两个步骤。
1.1类的生命周期指一个class文件从加载到卸载的全过程,这个过程包含7个部分
加载→验证→准备→解析→初始化→使用→卸载
其中,验证——准备——解析 称为连接阶段,除了解析外,其他阶段是顺序发生的,而解析可以与这些阶段交叉进行,因为Java支持动态绑定(晚期绑定),需要运行时才能确定具体类型;在使用阶段实例化对象。对象的生命周期只是类的生命周期中使用阶段的主动引用的一种情况(即实例化类对象)。
2.1加载java中我们常提起一个词语叫类加载,类加载和这里的加载并不是一回事。类加载指的是加载,连接,初始化三个阶段。回到加载。加载阶段,java虚拟机会做什么工作呢?
加载目的:找到需要加载的类并把类信息加载到方法区中。会在堆区实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。
类加载方式比较灵活,常用的有两种
- 根据类的全路径名找到相应的class文件,然后从class文件中读取文件内容
- 另一种是从jar文件中读取
- 从网络中获取:比如10年前十分流行的Applet。
- 根据一定的规则实时生成,比如设计模式中的动态代理模式
对于加载的时机取决于所用jvm。但是一定会在类将要被使用前进行加载。常用的hotspot是用到时才加载
2.2连接连接阶段可以和加载阶段交叉进行,即:加载阶段总是在连接阶段之前开始,连接阶段总是在加载阶段完成之后完成。但是加载阶段总是在连接阶段之前开始,连接阶段总是在加载阶段完成之后完成。连接阶段可以分为验证、准备、解析
- 验证:类被加载后,需要验证这个类是否合法。比如这个类是不是符合字节码的格式。这个阶段的目的就是保证加载的类是能够被jvm所运行。
- 准备:为类的静态变量分配内存并设置jvm默认的初值(并不是代码中设定的初值),常量的默认值为我们程序中设定的值
- 解析:把常量池中的符号引用转化为直接引用,就是jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址
连接阶段完成后会根据使用情况(直接引用还是被动引用)来选择是否对类进行初始化
直接引用会触发类的初始化,被动方式不会触发类的初始化
- 直接引用情况:
- new、读写类的静态变量、调用静态方法
- 通过反射执行以上三种行为
- 初始化子类,会触发父类的初始化
- 作为程序入口直接运行时
类的初始化过程:
按照顺序自上而下运行类中的变量赋值语句和静态语句,如果有父类,则首先按照顺序运行父类中的静态赋值语句和静态语句。而没有static修饰的赋值语句和执行语句在实例化对象的时候才会运行。
- 被动引用情况:
- 引用父类的静态字段,子类不会初始化
- 定义类数组
SubInitClass[] sc = new SubInitClass[10];
- 引用类的常量
类使用完后就可以卸载,但需要满足以下条件
- 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
- 加载该类的ClassLoader已经被回收
- 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法
如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载
类的卸载过程其实就是在方法区中清空类信息,java类的整个生命周期就结束了
参考文档:https://blog.csdn.net/zhengzhb/article/details/7517213
1.2对象的生命周期Java虚拟机会调用方法完成类实例化对象的初始化,这个方法会优先按照代码中顺序完成对类内部个属性的初始化,之后再调用类的构造函数(如果有父类,则优先调用父类的构造函数)
对象的生命周期只是类的生命周期中使用阶段的主动引用的一种情况,一个类实例化前一定完成了类的加载
对象周期分为如下阶段
2. 对象的生命周期Java虚拟机会调用方法完成类实例化对象的初始化,这个方法会优先按照代码中顺序完成对类内部个属性的初始化,之后再调用类的构造函数(如果有父类,则优先调用父类的构造函数)
对象的生命周期只是类的生命周期中使用阶段的主动引用的一种情况,一个类实例化前一定完成了类的加载
对象周期分为如下阶段
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jmDhsZqn-1656573275900)(https://secure2.wostatic.cn/static/emteQicbKYHEdC7GXYgS6D/image.png)]
2.1创建阶段此阶段主要目的是:进行类的实例化,创建java对象,主要有以下步骤:
- 为对象分配内存空间
- 构造对象
- 超类成员变量按顺序初始化,递归调用超类的构造方法。
- 子类成员变量按顺序初始化,子类构造方法调用
创建对象的方式
- 使用new
- 反射
- 对象clone
- 使用序列化
一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段
2.2应用阶段对象至少被一个强引用持有着。除非显示声明,否则所有对对象的引用都是强引用
有哪些引用类型?
强引用,软引用,弱引用,虚引用
- 强引用
强引用(Strong Reference)是指JVM内存管理器从根引用集合(Root Set)出发遍寻堆中所有到达对象的路径。当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用。
- 软引用
软引用(Soft Reference)的主要特点是具有较强的引用功能。只有当内存不够的时候,才回收这类内存,因此在内存足够的时候,它们通常不被回收
- 弱引用
弱引用(Weak Reference)对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象, GC总是进行回收。因此Weak引用对象会更容易、更快被GC回收
- 虚引用
2.3不可见阶段虚引用(Phantom Reference)的用途较少,主要用于辅助finalize函数的使用。Phantom对象指一些执行完了finalize函数,并且为不可达对象,但是还没有被GC回收的对象
我们在其他区域的代码中已经不可以再引用它,其强引用已经消失。通俗点说就是变量不能超范围使用,如下
public void process () {
try {
Object obj = new Object();
obj.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
while (isLoop) { // ... loops forever
// 这个区域对于obj对象来说已经是不可视的了
// 因此下面的代码在编译时会引发错误
obj.doSomething();
}
}
2.4不可达阶段
处于不可到达阶段的对象,在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用。处于不可到达阶段的对象,在虚拟机所管理的对象引用根集合中再也找不到直接或间接的强引用。其实所有垃圾回收算法所面临的问题是相同的——找出由分配器分配的,但是用户程序不可到达的内存块。与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用
如何判断一个对象是否还被需要呢?其实唯一的准则就是是否还有变量引用它。
我们使用引用计数法和可达性分析进行判断
引用计数法这是判断对象是否被引用最直接的一种方法,即给每个对象添加一个引用计数器,当该对象被引用一次时,该对象的计数器就加一,引用失效时就减一当为0时,意味着不可能再被赋予其他引用了。该方法最大的问题在于无法解决相互循环引用。
将对象被引用看做一个图,从堆中该对象出发,在可达性分析算法中,找到的这个变量被称为“GC Roots”,使用的遍历方法可能是深度优先遍历或者广度优先遍历,若无法找到一个变量引用则说明该对象是不可用的。
2.4收集阶段当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”
2.5终结阶段当对象执行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。
2.6对象空间重新分配垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”



