满嘴都是糖果

对象的创建

当虚拟机遇到一条字节码new指令时,虚拟机的步骤:

  1. 检查这个指令的参数是否能在常量池中定位大盘一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、 解析和初始化过。 如果没有, 那必须先执行相应的类加载过程

  2. 在类加载检查通过后, 接下来虚拟机将为新生对象分配内存,对象所需内存的大小在累加载完成之后可完全确定。

    假设Java堆中内存是绝对规整的,那么可以将指针向空闲空间方向挪动一段对象大小的距离,这称为指针碰撞;但如果Java堆中的内存并不是规整的, 已被使用的内存和空闲的内存相互交错在一起 ,虚拟机会维护一个空闲列表,从列表中找到一块空间划分给对象实例。Java堆是否规整由垃圾处理器是是否带有空间压缩整理能力决定。

    对象创建在虚拟机中是非常频繁的行为,修改指针位置存在线程安全问题。处理措施分类两种:分配内存空间做同步处理——CAS机制;在堆内为每个线程分配一块私有的内存区域,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB),只有缓冲使用完了,分配新的缓存区时需要同步锁定。虚拟机是否使用TLAB, 可以通过-XX: +/-UseTLAB参数来设定。

  3. 内存分配完成之后,虚拟机将分配到的内存空间(不包含对象头)都初始化为零值,如果使用了TLAB的话,这个工作可以提前至TLAB分配时进行。这部操作确保了对象的实例字段在Java代码中可以在不赋初值就直接使用。

  4. 设置对象头中的数据,例如这个对象是哪个类的实例、 如何才能找到类的元数据信息、 对象的哈希码、 对象的GC分代年龄等信息。

对象的内存布局

对象在堆内分为三部分:对象头、实例数据和对齐填充。

对象头包含两类信息:

实例数据:在程序代码里面所有定义的各种类型的字段内容都必须记录。这部分的存储顺序受到虚拟机分配策略参数(-XX:FieldsAllocationStyle 参数)和Java源码中定义顺序的影响。HotSpot虚拟机默认的分配顺序为longs/doubles、 ints、 shorts/chars、 bytes/booleans、 oops(OrdinaryObject Pointers, OOPs) , 从以上默认的分配策略中可以看到, 相同宽度的字段总是被分配到一起存放, 在满足这个前提条件的情况下, 在父类中定义的变量会出现在子类之前。 如果HotSpot虚拟机的+XX: CompactFields参数值为true(默认就为true) , 那子类之中较窄的变量也允许插入父类变量的空隙之中, 以节省出一点点空间。

对齐填充 :仅仅起着占位符的作用。 由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍, 换句话说就是 任何对象的大小都必须是8字节的整数倍。 对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍) , 因此, 如果对象实例数据部分没有对齐的话, 就需要通过对齐填充来补全。

对象的访问定位

分为两种方式:使用句柄和直接指针