[读书笔记] 有关Java对象的一切

2019-07-29  本文已影响0人  hugo54

一、对象的创建过程

(此处讨论的对象限于普通Java对象,不包括数组和Class对象等)

当JVM遇到一条new指令时,JVM会根据以下流程创建一个新的对象:

  1. 类加载检查:检查这个指令的参数是否能在常量池中定位到类的符号引用,检查符号引用代表的类是否被加载、解析和初始化过。如果没有,执行类加载。

  2. 为新对象分配内存:类加载完成后,对象所需内存大小便已确定。为对象分配内存等同于把一块确定大小的内存从Heap中划分出来。(随着JIT编译器的发展和逃逸分析技术逐渐成熟,产生了栈上分配、标量替换等优化技术,因而并不是所有对象都在Heap上分配)

  3. 内存初始化:将分配到的内存空间都初始化为零值(不包括对象头)。

  4. 设置对象头信息:例如该对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等。

  5. 执行<init>方法: At the level of the Java Virtual Machine, every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name <init>.

为新对象分配内存的方式由Heap是否规整决定:

而Heap是否规整又由所采用的垃圾收集器(Garbage Collector)决定。下面是几种常用GC的内存分配方式:

另外,JVM为对象分配内存时,在并发情况下要考虑线程安全。这个问题有两种解决方案:

二、对象的内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可分为三块区域:对象头、实例数据和对齐填充。

对象头(Header)

对象头包括两部分信息:一部分用于存储对象自身的运行时数据,例如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针确定对象是哪个类的实例。

实例数据(Instance Data)

实例数据部分是对象真正存储的有效信息,也是程序代码中所定义的各种字段内容,其中包括从父类继承的字段和子类中自行定义的字段。

对齐填充(Padding)

由于HotSpot VM的自动内存管理系统要求对象的起始地址必须是8字节的整数倍(对象大小必须是8字节的整数倍),而且Header部分正好是8字节的整数倍。所以当Instance Data长度没有对齐时,需要做对齐填充。

三、对象的访问定位

建立对象是为了使用对象,Java程序通过Stack上的reference数据来操作Heap上的具体对象。对象访问的方式取决于虚拟机实现,主流的访问方式有句柄直接指针

如果使用句柄的话,那么Java堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄的好处是:reference中存储的稳定的句柄地址,对象被移动时只会改变对象实例指针,reference本身不需要修改。

如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何防止访问类型数据的相关信息,reference 中存储的直接就是对象的地址。使用直接指针的好处是:速度更快,节省了一次指针定位的时间开销。

四、对象一生所在的内存区域

五、参考书目

周志明《深入理解Java虚拟机(第2版)》

上一篇下一篇

猜你喜欢

热点阅读