【深入理解Java虚拟机1-笔记】HotSpot对象探秘

1. 对象的创建

虚拟机遇到一个new指令时

  1. 检查这个指令的参数是否能在常量池定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,则必须先执行相应的类加载过程。
  2. 对新生对象分配内存
    1. 内存是绝对规整的: Bump the Pointer指针碰撞。就是仅仅把内存分界指针挪动一段与对象大小相等的距离。
    2. 内存不是规整的: Free List通过空闲列表中找到足够大的空闲内存
  3. 将分配到的内存空间初始化为0(不包括对象头)
  4. 虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些都存储在对象头(Object Head)中

关于内存分配

GC收集器 内存分配算法
Serial、ParNew(带Compact过程的收集器) 指针碰撞
CMS(基于Mark-Sweep) Free List

问题:对象创建在虚拟机中是非常频繁的,即使是仅仅修改一个指针,在并发的情况下也不一定是安全的。
解决:

  • 同步处理:CAS
  • 将内存分配的操作划分在不同的空间中,即每个线程在Java堆中预先分配一小块内存,成为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)

虚拟机对TLAB使用-XX:+/-UseTLAB参数来配置

2. 对象的内存布局

  • 对象头(Object Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

对象的访问定位

建立对象是为了使用,我们在Java程序中使用栈上的reference数据来操作堆上的具体对象。Java在虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆中的具体位置,所以这是取决于虚拟机实现而定的。目前主流方式有两种:

  • 句柄

    Java堆中将会划分中一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄包含了对象实例数据与类型数据各自的地址信息。好处是移动对象时,只需要移动句柄,而不是reference。

  • 直接指针

    堆中对象实例包含了类型数据的指针信息,reference中存储的就是对象实例地址。