深入理解Java虚拟机学习笔记2-JVM中对象布局

一、对象的内存布局

在Hotspot中,对象在内存中存储布局可以分为三块: 对象头(Header) 、实例数据(Instance Data)、对齐填充(Padding)

二、对象头

1、对象运行时数据: 哈希吗、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳;官方成为MarkWord,一种非固定数据结构,根据对象状态标志位复用存储空间存储对象数据的结构。

2、类型指针:指向类元数据的指针

三、实例数据

对象真正存储的有效信息,也就是代码中定义各种类型的字段内容。

四、对齐填充

Hotspot虚拟机自动内存管理系统要求对象起始地址必须是8字节的整数倍,因此对象的大小必须是8字节的整数倍。当对象实例数据没有对齐时候,需要通过对齐填充补全。

五、内存中对象的创建

1. new一个对象  --首先检查运行时常量池中是否能定位这个类的符号引用,并检查这个符号引用代表的类,是否已被加载、解析、初始化过。如果没有,进行类加载。

2.类加载通过后,虚拟机为新生对象分配内存。对象所需的内存大小在类加载后便可以确定。java堆中分配内存,有两种方式,指针碰撞和空闲列表,方式的选择,取决于堆是否规整,对是否规整取决于采用垃圾收集器是否带有压缩整理功能决定。因此Serial ParNew 等带compact过程的收集器,采用指针碰撞,CMS这种基于Mark-Sweep算法的垃圾收集器,采用空闲列表。

3.内存分配完毕,虚拟机需要将分配到内存空间都初始化零值。保证对象实例字段在java代码中,不被赋初始化就可以被直接使用。

4.再下一步,虚拟机对对象进行必要的设置,例如对象是哪个类的实例,如果才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等,还有根据当前运行状态,是否启用偏向锁等。

5.到这一步,从虚拟机视角看,一个对象产生完毕,但是从程序员角度看,才刚刚开始, 方法还没有执行,所有字段都还为零。一般执行new指令之后,会接着执行方法,自定义初始化完成。

注意:在第2步,分配内存,为了保证并发情况下对同一块内存分配的线程安全,有两种方式,一种是为每个线程分配一小块内存,成为本地线程分配缓冲TLAB。启用的话(参数-XX:+UseTLAB开启TLAB),默认是开启的,对象先在TLAB上分配,TLAB不够用,才会锁定,提升效率。另外一种是采用CAS配上失败重试的方式,保证分配内存更新指针操作的原子性。

六、对象的访问定位

1.使用句柄

2.使用指针

使用句柄的优点是,栈帧本地变量表中存储的reference是稳定句柄地址,对象被移动时,只改变句柄中实例数据指针,在对象移动时候,只需改变句柄中实例数据指针,而reference不需要修改,适用于垃圾收集时候移动对象非常普遍的情况下。

使用指针最大好处就是速度快,使用句柄需要reference定位句柄池中数据,再指向堆中对象,但是直接指针,直接从栈帧本地局部变量表中reference指向堆中对象,在访问java对象比较频繁的情况下,可以节省一次指针定位时间开销,积小成多效率可观。Hotspot使用直接指针。

 

tips:发现一个写的比较好的,对象创建时候内存分配过程描述文章~~   

地址 https://blog.csdn.net/zyc88888/article/details/80361635

 

 

你可能感兴趣的:(java虚拟机)