阅读内容前提为

  • 虚拟机:HotSpot
  • 内存区域:Java堆
  • 内容: 对象分配、布局和访问的全过程
  • JDK版本:1.7

对象的创建

创建对象(例如克隆、反序列化)通常仅仅是一个new关键字而已。对应在JVM中,对象(普通Java对象,不包含数组和Class对象等)的创建是怎样的?

类加载检查

  • 参数是否能在常量池中定位到一个类的符号引用
  • 这个符号引用代表的类是否已经被加载、解析和初始化过
    • 无:执行相应的类加载过程

分配内存

  • 对象所需内存的大小在类加载后便可完全确定
  • 为新生对象分配空间:从Java堆中划分出确定大小的内存
分配方式
  • 指针碰撞Bump the Pointer

Java堆中内存是绝对规整的,用过的内存在一边,空间内存在另外一边,中间用指针作为分界点的指示器,分配内存就是仅仅把指针向空闲内存挪动一段与对象大小相等的距离。

  • 空闲列表Free List

Java堆中内存是不规整的,已使用的内存和空闲内存相互交错,虚拟机需维护一个列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。

分配方式的选择:垃圾收集器是否带有压缩整理功能(决定Java堆是否规整)

  • Serial、ParNew等带Compact过程的收集器
    • 指针碰撞
  • 基于Mark-Sweep算法的CMS收集器
    • 空闲列表
并发情况下对象创建非线程安全的问题

对象创建行为非常频繁,并发情况下可能出现:正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存。

  • 对分配内存的动作进行同步处理
    • 虚拟机采用CAS+失败重试方法保证更新操作的原子性
  • 把内存分配的动作按照线程划分在不同的空间之中进行
    • 每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Loca Allocation Buffer,TLAB)
    • 哪个线程需要分配内存就在对应的TLAB中进行
    • 只有在TLAB用完并重新分配新的TLAB时,才需要同步锁定
    • 虚拟机是否使用TLAB
      • -XX:+/-UseTLAB参数设置

内存空间初始化为零值(不包括对象头)

  • 如果使用TLAB,可以提前至TLAB分配时进行此操作
  • 保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,访问到的字段的数据类型都是零值

对对象的对象头Object Header进行设置

  • 设置方式由虚拟机当前的运行状态决定
    • 是否启用偏向所等
  • 对象头包含内容
    • 这个对象时哪个类的实例
    • 如何才能找到类的元数据信息、哈希码、GC分代年龄等

可选:执行方法

至此,

  • 从虚拟机的角度看
    • 新对象已经产生了
  • 从Java程序的角度看
    • 对象创建才刚刚开始
      • 方法还没有执行
      • 所有的字段都还为零

由字节码中是否跟随invokespecial指令所决定是否执行方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

Markdown