深入理解JVM第二章:自动内存管理

一、运行时数据区

前面两个部分都是了解了的。

3. HotSpot虚拟机对象探秘:

3.1 对象的创建

  1. JVM遇到一个new关键字后:

    • 在常量池中看能够定位 类的符号引用, 并检查这个符号引用代表的类是否被加载,解析和初始化过。没有的话就加载这个类;
  2. 接着为新生对象分配内存:这里讨论了三种分配内存的方式

  • 假设内存绝对规整: 一端使用过的,另一端是没用过的: 把指针向空闲的那边移动需要空间大小就好 (指针碰撞)
  • 使用过的内存和空闲内存交错在一起, 就需要维护一个内存空闲列表; 从中挑选出需要大小的一片内存,进行分配;
  • 给每个线程划分一块自己的私有内存区域: 本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)
    • 虚拟机使用TLAB: -XX:+/-UseTLAB
  1. JVM给对象进行必要的设置: Object.Class, Hashcode, 类的元数据信息, 对象的GC分代年龄 ( 放在对象的对象头里)
  2. 这些做完JVM就创建了一个对象; 但JAVA程序才开始用构造函数初始化这个实例化对象

3.2 对象的内存布局

一个对象在堆中的存在可以划分为三个部分:对象头(Header),实例数据(Instance data)和对齐填充;

  1. 对象头

    • 存储两类信息“
      1. 对象自身的运行时数据
      2. 类型指针:对象指向它的类型元数据的指针;(可确定这个对象是哪个类的实例)
  2. 实例数据

    • 真正存储对象实例信息的地方,无论父类的还是子类定义的都得放在这里面;
    • 存储顺序受虚拟机分配策略参数(-XX:FieldsAllocationStyle 参数)和字段在Java源码中定义顺序的影响;
  3. 对齐填充

    对象必须是8字节的倍数;所以满足这个要求就填充一下;

3.3 对象的访问定位

java会通过栈上的 reference 数据来操作堆上的对象。

主流的访问方式主要有使用句柄和直接指针两种方式。

  • 使用句柄访问,java堆中就会划分出一块内存作为句柄池 ,reference 保存的就是句柄地址。而句柄包含了 对象实例数据对象类型数据 各自的具体地址信息;
  • 直接用指针访问的话, Java堆中对象的内存布局就必须考虑 对象类型数据 的相关信息(地址)放在哪里;