• JVM—对象的创建流程与内存分配


    JVM—对象的创建流程与内存分配

    创建流程

    对象创建的流程图如下:

    对象的创建流程图

    对象的内存分配方式

    内存分配的方式有两种:

    • 指针碰撞(Bump the Pointer)
    • 空闲列表(Free List)
    分配方式 说明 收集器
    指针碰撞(Bump the Pointer) 内存地址是连续的(新生代) Serial和ParNew收集器
    空闲列表(Free List) 内存地址不连续(老年代) CMS收集器和Mark-Sweep收集器

    内存分配方式1

    内存分配方式2

    指针碰撞

    指针碰撞示意图如下:

    指针碰撞示意图

    内存分配安全问题:

    虚拟机给A线程分配内存的过程中,指针未修改,此时B线程同时使用了该内存,就会出现问题。

    解决方式:

    • CAS乐观锁:JVM虚拟机采用CAS失败重试的方式保证更新操作的原子性;
    • TLAB (Thread Local Allocation Buffer)本地线程分配缓存,预分配。

    分配主流程

    首先从TLAB里面分配,如果分配不到,再使用CAS从堆里面划分。

    对象如何进入老年代

    对象进入老年代流程如下:

    对象如何进入老年代

    • 新对象大多数默认都进入Eden;

    • 对象进入老年代的四种情况:

      • 年龄太大 MinorGC15次-XX:MaxTenuringThreshold】;

      • 动态年龄判断:MinorGC后会动态判断年龄,将符合要求对象移入老年代;

        MinorGC之后,发现Survivor区中的一批对象的总大小大于了这块Survivor区的50%,那么就会将此时大于等于这批对象年龄最大值的所有对象,直接进入老年代。

        例子: Survivor区中有一批对象,年龄分别为年龄1+年龄2+年龄n的多个对象,对象总和大小超过了Survivor区域的50%,此时就会把年龄n及以上的对象都放入老年代。希望那些可能是长期存活的对象,尽早进入老年代。
        比率可以由-XX:TargetsurvivorRatio指定
        
      • 大对象直接进入老年代1M【-XX:PretenureSizeThreshold 】;(前提是Serial和ParNew收集器)

        为了避免大对象分配内存时的复制操作降低效率。

        避免了Eden和Survivor区的复制。

      • MinorGC后存活对象太多无法放入Survivor。

    空间担保机制

    空间担保机制:当新生代无法分配内存的时候,我们想把新生代的老对象转移到老年代,然后把新对象放入腾空的新生代。此种机制我们称之为内存担保。

    空间担保流程图如下:

    空间担保流程图

    对象内存布局

    对象内存布局示意图如下:

    对象内存布局

    对象里的三个区

    堆内存中,一个对象在内存中存储的布局可以分为三块区域:

    堆内存中,一个对象在内存中存储的布局可以分为三块区域:

    • 对象头(Header) : Java对象头占8byte。如果是数组则占12byte。因为JVM里数组size需要使用4byte存储。

      • 标记字段MarkWord:

        • 用于存储对象自身的运行时数据,它是synchronized实现轻量级锁和偏向锁的关键。

        • 默认存储:对象HashCode、GC分代年龄、锁状态等等信息。

        • 为了节省空间,也会随着锁标志位的变化,存储数据发生变化。

          锁标志位的变化

      • 类型指针KlassPoint:

        • 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例
        • 开启指针压缩存储空间4byte,不开启8byte。
        • JDK1.6+默认开启
      • 数组长度:如果对象是数组,则记录数组长度,占4个byte,如果对象不是数组则不存在。

      • 对齐填充:保证数组的大小永远是8byte的整数倍。

    • 实例数据(Instance Data):生成对象的时候,对象的非静态成员变量也会存入堆空间

    • 对齐填充(Padding) :JVM内对象都采用8byte对齐,不够8byte的会自动补齐。

    如何访问一个对象

    有两种方式:

    1. 句柄:稳定,对象被移动只要修改句柄中的地址

    2. 直接指针:访问速度快,节省了一次指针定位的开销

    句柄方式访问对象

    通过直接指针访问对象

  • 相关阅读:
    Pytorch解决 多元回归 问题的算法
    R语言使用pt函数生成t分布累积分布函数数据、使用plot函数可视化t分布累积分布函数数据(t Distribution)
    哈希表超详解
    煤矿企业如何选择合适的设备健康管理系统
    USB3.0:VL817Q7-C0的LAYOUT指南(二)
    如何选择最适合你的LLM优化方法:全面微调、PEFT、提示工程和RAG对比分析
    识别人体神经网络的软件,识别人体神经网络系统
    Serverless成技术新常态:降低开发门槛,让效率更快一点
    Java编码与解码
    2024年1月京东奶粉行业销售数据分析(TOP10奶粉品牌排行榜)
  • 原文地址:https://www.cnblogs.com/nicaicai/p/18118435