HiSEN

Java对象的一生 | 从new到被回收

一、出生过程

这里讲述的是第一次出生的过程,即之前class没有被加载。

1.1 类初始化

1.1.1 加载

1.1.1.1 通过类的全限定名获取定义此类的二进制字节流(可以从zip包、网络、运行时动态生成);
1.1.1.2 将这个字节流所代表的静态存储结构转化为方法去运行时数据结构;
1.1.1.3 在内存(方法区)中生成一个代表这个类的java.lang.Class对象,作为方法区这个类各种数据访问的入口;

1.1.2 验证

1.1.2.1 文件格式验证

1.1.2.1.1 魔数是否以0xCAFEEBABE开头(咖啡宝贝)
1.1.2.1.2 主、次版本号是否在当前虚拟机处理的范围之内(不同的jdk版本编译出来的版本不一致,可向前兼容)
1.1.2.1.3 常量池是否有不被支持的类型(检查常量tag标志)
1.1.2.1.4 指向常量的各种索引值是否指向不存在或者不符合类型的常量
1.1.2.1.5 CONSTANT_Utf8_info型的常量中是否有不符合UTF-8编码的数据
1.1.2.1.6 Class文件中各个部分以及文件本身是否有被删除或者附加的其它信息

这些操作是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身安全

1.1.2.2 元数据验证

1.1.2.2.1 是否有父类(Object除外)
1.1.2.2.2 是否继续了不允许继承的类(被final修饰的)
1.1.2.2.3 如果当前不是抽象类,是否实现了其父类或接口中要求实现的所有方法
1.1.2.2.4 类中的字段、方法是否与父类产生矛盾(如覆盖父类final字段、不合法的重载)
这些操作是对字节码进行语义分析,确保符合Java语言规范要求

1.1.2.3 字节码验证

1.1.2.3.1 确保操作数栈的数据类型与指令代码序列能完美配合(反例:操作数栈为int,使用的时候按long加载)
1.1.2.3.2 确保跳转指令不会跳转到方法体以外的字节码指令上
1.1.2.3.3 确保方法体重点类型转换是有效的
这些操作主要是通过数据流和控制流分析,确定程序的语义是合法的、符合逻辑的。
JDK1.6之后有一个优化,利用StackMapTable来验证是否合法

1.1.2.4 符号引用验证

1.1.2.4.1 符号引用中通过字符串描述的全限定名是否能找到对应的类
1.1.2.4.2 符号引用中的类、字段、方法是否可以被当前类访问
在将符号引用转化为直接引用的时候触发符号引用验证

1.1.3 准备

1.1.3.1 仅为类变量分配内存,并且赋值为初始值,例如int为0(被static修饰的)
1.1.3.2 特殊情况,被final修饰的static变量会直接赋值为代码给定的值
准备阶段是正式为类变量分配内存并设置类变量初始值,这些变量所使用的内存都将在方法区中进行分配。

1.1.4 解析

虚拟机将常量池中的符号引用替换为直接引用
符号引用:一组用来描述所引用目标的符号,所引用的目标不一定在内存中
直接引用:直接指向目标的指针、相对偏移量、能间接定位到句柄,直接引用的目标必须在内存中存在

1.1.5 初始化

<client>()方法:由编译器自动收集类中所有变量的赋值动作和静态语句块(static{}块)中的语句合并产生
按顺序收集。父类的此方法先执行,虚拟机会保证线程安全
真正开始执行类中定义的Java代码,

1.2 类实例化(New)

1.2.1 分配内存:在类初始化完成之后,能知道所需要的内存大小

指针碰撞:内存规整的,中间指针划分已用、空闲,那么分配就是指针向空闲一方移动对象大小相等的距离
空闲列表:内存不规整,用列表维护一个可用内存地址,从列表中找出一个合适大小的空间分配给实例

1.2.2 初始化为0值

1.2.3 设置对象头信息

1.2.4 执行<init>()方法,按照程序员的意思给对象赋值

<init>()方法:实例构造器

类的初始化是指类加载过程中的初始化阶段对类变量按照程序猿的意图进行赋值的过程;
类的实例化是指在类完全加载到内存中后创建对象的过程。

二、生平事迹

听候线程的指令,执行相关方法

三、驾鹤西去

可达性分析算法:从GC Root节点开始向下搜索,搜索所走过的引用链为引用链,当没有任何引用链时说明对象不可达
经历过两次不可达标记才会被标记为可回收
堆内存回收主要发生在堆上的新生代,为Minor GC,使用的是复制算法
新生代分为:Eden、to Survivor、from Survivor,后两者为Survivor,三者的比例为8:1:1

在Minor GC之前,to Survivor为空,对象存在:Eden、from Survivor
在Minor GC执行
Eden存活着的对象拷贝到to Survivor,同一时候对象年龄+1

from Survivor区中的幸存对象会考虑对象年龄
假设年龄没达到阈值,对象依旧拷贝到to survivor中
假设对象达到阈值那么将被移到老年代

复制阶段完毕后,Eden和from幸存区中仅仅保存死对象,能够视为清空
假设在复制过程中to幸存区被填满了,剩余的对象将被放到老年代

在Minor GC之后
from survivor和to survivor会调换一下名字,下次Minor GC时,to survivor变为from Survivor

四、参考连接

  1. 《深入理解Java虚拟机》
  2. 一个Java Class自述短暂的一生
  3. 一切皆对象:论对象的一生一世
  4. Java对象的生命周期与垃圾回收以及四种引用
  5. Java new一个对象的过程中发生了什么

目前写的不是太完善,有机会写的透彻明白一些。