类的一生分为七个阶段:加载,验证,准备,解析,初始化,使用,卸载。

前面五个阶段是类加载过程,类加载过程有且只有一次。

加载的工作内容是使用类的全限定名来获取定义一个二进制字节流,定义一个Class对象(在HotSpot虚拟机之中,这个对象存储进了方法区)。

验证的工作内容是为了确保字节流中的信息符合当前虚拟机的要求,并且不会伤害到虚拟机自身的安全。

准备是设置类变量的初始值并为其在方法区分配空间的阶段,这里的初始化不是指将程序员设定的值赋进去,而是0值。

解析是将符号引用换为直接引用。

初始化是类加载的最后一个过程,赋值类变量,执行静态代码块,执行类构造器。

可能会有疑惑,加载的时候创建了Class对象,可是静态变量分配空间是在准备阶段,难不成构造器是静态的码,这里请移步关于构造器是否是静态方法的讨论。

接下来就是平时真正创建对象的时候了,也就是我们的实例构造器或者说是构造方法了。

一个对象的引用会创建在栈空间上,对象则会分配在堆内存上。这时候是如何分配内存的呢,不可能直接放进去就行了,分配内存的时候有两种分配方式。第一种假设是内存非常的规整,用过的放在一边,没有用过的放在另一边,中间有一个指针作为分界线,每当需要分配内存的时候指针就会偏移与对象大小相同空间的位置给他,这个是“指针碰撞”。第二种是使用过的内存和没有使用过的交叉存放,虚拟机会维护一个列表,等到有新的对象要进来的时候,就会找一个足够他使用的内存给他,这个是“空闲列表”。这是内存分配的过程。

 

一个对象在内存中的布局由三个部分构成,对象头和实例数据还有对齐填充。

对象头当中存放了运行时数据,例如哈希码,GC分代年龄等等,还有类型指针。

实例数据是指存储的有效的信息,也就是在代码部分记录下来的信息,从父类中继承下来的,子类中定义的。

对其填充不是必须存在的,HotSpot要求对象的起始地址必须是8字节的整数倍,也就是说对象必须是8字节的在整数倍,这时候对齐填充就会把不足的部分补全。

 

对象分配内存的过程和结构部分说完了,那就说一下是如何查找这个对象的吧

我们可以通过句柄访问和直接访问。

1.句柄访问

java生成request对象_类加载

句柄访问会创建两个指针,一个是指向实例对象数据,一个指向对象类型数据。

2.直接指针

java生成request对象_java生成request对象_02

直接指针上创建了一个指针,指向对象类型数据。

关于数据的移动,句柄访问是不需要修改reference的,但是直接指针会需要修改,也就是修改的时候只需要修改实例数据指针就可以了,修改起来比较方便。

关于数据的访问,句柄访问需要定位两次指针,然而直接指针只需要定位一次,再创建了许多个对象之后,指定为一次指针所节省的开销还是非常大的。

关于对象的消亡过程请移步对象存活状态的判定以及死亡过程

参考书籍:《深入理解JAVA虚拟机》