《深入理解虚拟机》阅读笔记-对象内存的分配与布局

对象内存的分配与布局

  • 创建(new、clone、反射、反序列化)
    • new -> 常量池定位符号引用,并检查符号引用代表的类是否被加载、解析、初始化。若没有,则执行类加载过程
    • 对象所需内存在类加载完成后便可完全确定。
    • 对象内存分配方式,由垃圾收集器是否有压缩整理功能决定
      • 指针碰撞:堆内存绝对规整,使用的内存与空闲内存通过指针作为分界点。分配内存只需要相应移动指针即可。serial、parNew等带Compact过程的收集器。
      • 空闲列表:记录空闲内存地址。分配内存时同时更新列表即可。cms基于Mark-Swap算法的收集器。
    • 对象内存分配的线程安全问题
      • 分配内存时同步--虚拟机采用CAS加失败重试的方法
      • 本地线程分配缓冲(Thread Local Allocate Buffer)
        • 堆预先分配给线程一定的内存空间,分配内存时首先使用该部分。
        • 只有当TLAB使用完,重新分配时才同步。
        • 是否使用,通过 -XX:+/-UserTLAB参数决定
  • 内存布局
    • 对象头
      • MarkWord
        • 存放对象自身的运行时数据,比如hashcode、GC分代年龄、锁状态便知、线程持有的锁、偏向线程ID、偏向时间戳
        • 在32位、64位的虚拟机中(未开启压缩指针)分别为32bit和64bit
        • 非固定的数据结构,根据对象状态复用存储空间

      • Klass指针
        • 对象指向它的类元数据的指针,通过该指针确定对象是哪个类的实例
        • 并不完全通过对象本身,句柄池的句柄同时保存到实例数据和类型数据的指针
        • 若对象是Java数组,则需要一块记录数组长度的空间,因为无法通过普通Java对象的元数据信息确定
    • 实例数据
      • 对象真正存储的有效信息,即定义的各种类型字段内容
      • 存储顺序受虚拟机分配策略和字段在源码中的定义顺序影响
        • HotSpot默认分配(相同宽度的字段一起分配):longs/doubles、ints、shorts、chars、bytes/booleans、oops(ordinary object pointers)
        • 父类定义的字段在子类之前
        • 若CompactFields参数值为true,则 子类较窄的变量会被插到父类变量的空隙间
    • 对齐填充
      • 占位符作用
      • Hotspot规定对象的起始地址必须是8字节的整数倍,即对象必须是8字节的整数倍
      • 对象头是8字节的倍数(1倍或2倍),因此该部分是在需要时补齐实例数据的空缺
  • 访问定位
    • JVM通过栈上的reference数据操作堆上的对象,其本质只是指向对象的引用
    • 方式由虚拟机决定,目前主流的两种
      • 句柄访问
        • 堆中划分内存作为句柄池,reference存放对象的句柄地址
        • 句柄包括对象实例数据与类型数据的地址信息
        • 优点是稳定,当对象被移动时,只会更改句柄的实例数据地址
      • 直接指针访问(Hotspot)
        • reference存放的对象地址
        • 而相应的堆对象必须存储类型数据的相关信息(Klass指针)
        • 优点是高效,节省了一次指针定位的时间
发布了24 篇原创文章 · 获赞 0 · 访问量 104

猜你喜欢

转载自blog.csdn.net/jiangxiayouyu/article/details/105614174
今日推荐