一、运行时数据区
前面两个部分都是了解了的。
3. HotSpot虚拟机对象探秘:
3.1 对象的创建
JVM遇到一个
new
关键字后:- 在常量池中看能够定位
类的符号引用
, 并检查这个符号引用代表的类是否被加载,解析和初始化过。没有的话就加载这个类;
- 在常量池中看能够定位
接着为新生对象分配内存:这里讨论了三种分配内存的方式
- 假设内存绝对规整: 一端使用过的,另一端是没用过的: 把指针向空闲的那边移动需要空间大小就好 (指针碰撞)
- 使用过的内存和空闲内存交错在一起, 就需要维护一个内存空闲列表; 从中挑选出需要大小的一片内存,进行分配;
- 给每个线程划分一块自己的私有内存区域: 本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)
- 虚拟机使用TLAB:
-XX:+/-UseTLAB
- 虚拟机使用TLAB:
- JVM给对象进行必要的设置: Object.Class, Hashcode, 类的元数据信息, 对象的GC分代年龄 ( 放在对象的
对象头
里) - 这些做完JVM就创建了一个对象; 但JAVA程序才开始用构造函数初始化这个实例化对象
3.2 对象的内存布局
一个对象在堆中的存在可以划分为三个部分:对象头(Header),实例数据(Instance data)和对齐填充;
对象头
- 存储两类信息“
- 对象自身的运行时数据
- 类型指针:对象指向它的类型元数据的指针;(可确定这个对象是哪个类的实例)
- 存储两类信息“
实例数据
- 真正存储对象实例信息的地方,无论父类的还是子类定义的都得放在这里面;
- 存储顺序受虚拟机分配策略参数(-XX:FieldsAllocationStyle 参数)和字段在Java源码中定义顺序的影响;
对齐填充
对象必须是8字节的倍数;所以满足这个要求就填充一下;
3.3 对象的访问定位
java会通过栈上的 reference 数据来操作堆上的对象。
主流的访问方式主要有使用句柄和直接指针两种方式。
- 使用句柄访问,java堆中就会划分出一块内存作为
句柄池
,reference 保存的就是句柄地址。而句柄包含了对象实例数据
和对象类型数据
各自的具体地址信息; - 直接用指针访问的话, Java堆中对象的内存布局就必须考虑
对象类型数据
的相关信息(地址)放在哪里;