JVM:对象的创建过程

281 阅读3分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

Java对象创建步骤:

1.new指定

  • Java虚拟机接收到一条new指令时:
    • 首先:检查这个指令的参数是都能在常量池中定位到一个类的符号引用
    • 并且检查这个符号引用代表的类是否已经被类加载、解析、和初始化过。
      • 无:执行相应的类加载过程
      • 有:分配内存 流程图如下:

image.png

2.分配内存

  • 分配内存为对象分配空间等同于把一块确定的内存从Java堆中分离处理

2.1 情况1:内存规整

  • 所有被使用的内存放一边,未使用的在另外一边,中间放着一个指针作为分界点的指示器
    • 内存分配策略: -->> “指针碰撞 Bump The Pointer”
      • 过程说明:分配内存的过程是把那个指针指向空闲空间方向挪动一段与对象大小相等的距离,如图所示:

2.2 情况2:内存不规整

  • 已被使用的内存和空闲的内存交错在一起
  • 内存分配策略: -->> “空闲列表 Free List”
    • 过程说明:虚拟机通过维护一个表来记录哪些块可用,哪些不可用,从中找出一块足够大的空间分配该实例并记录在表中。

2.3 内存分配策略选择

  • 选择哪种分配方式有Java堆是否规整所决定,而java堆是否规整又由所采用的垃圾回收器所否带有空间压缩整理(Compact)的能力决定。
  • Serial、PaeNew 等 带有压缩整理过程 -->> 采用的是 指针碰撞
  • CMS 基于清除(Swap)算法收集器 不带有压缩整理过程 -->> 采用的是 空闲列表

2.4 TLAB

  • TLAB(Thread Local Allocation Buffer)
  • 现象描述:对象的创建在虚拟机中是特别频繁的,即使修改一个指针的指向位置在并发情况下线程也会是不安全的,可能出现给A分配内存,指针还没来得及修改,对象B同时使用了原来的指针来分配内存的情况
  • 解决方案1:分配内存空间的动作进行同步处理,虚拟机采用CAS配上失败重试的方式保证更新操作的原子性
  • 解决方案2:分配内存空间的动作按照线程划分在不同的空间进行,即:每个线程在java堆中预先分配一小块内存,成为本地线程分配缓冲(TLAB),那个线程需要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。
  • 虚拟机是否使用TALB,通过 -XX:+/- UseTLAB 参数设定

2.5 内存空间初始化零值

  • 内存分配后,虚拟机需将分配后的内存空间(不包括对象头)都初始化为零值,
    • 备注:如果使用了TLAB,这项工作可以提前至TLAB分配时进行
  • 本步操作保证java代码中的可以不赋值就直接使用,使程序访问到这些字段的数据类型所对应的零值。

2.6 对对象头进行设置

  • 对象是那个类的实例?如何才能找到类的元数据信息?对象GC分代年龄等信息..
  • 这些信息存放在对象的对象头(Object Header)之中,根据虚拟机当前的运行状态 如是否启用偏向锁等,对象头会有不同的设置方式,此时从JVM角度来看,一个新的对象已经产生了

2.7 执行init()方法

  • 执行构造方法 即 Class文件中()
  • .....

2.8 流程步骤

image.png

参考链接: