当User user = new User()时,JVM 做了什么

Java程序的初始化顺序

一、对象的访问

Java 中,操作一个对象是通过指向这个对象的引用。对象存在堆中,这个引用存在虚拟机栈中。那么引用通过什么方式去定位堆中对象的位置呢?

1️⃣直接指针法(HotSpot 实现):引用中直接存储的就是堆中对象的地址。好处就是一次定位速度快,缺点是对象移动(GC 时对象移动)引用本身需要修改。

2️⃣句柄法:Java 堆中划分出一部分作为句柄池,引用存储的是对象的句柄地址,而句柄中包括了对象实例和类型的具体位置信息。好处是对象移动只会改变句柄中的实例数据指针,缺点是两次定位。

二、创建对象流程

1️⃣当虚拟机遇到一条 new 指令时,会去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并检查代表的类是否已经被类加载器加载。如果没有被加载那么必须先执行这个类的加载。
2️⃣类加载检查通过后,虚拟机将为新对象分配内存,对象所需内存的大小在类加载后便可以确定。
3️⃣内存分配完成后,虚拟机需要将对象初始化为零值,保证对象的实例变量在代码中不赋初始值就能直接使用。类变量在类加载的准备阶段初始化为零值。
4️⃣对对象头进行必要信息的设置,比如如何找到类的元数据信息、对象的 HashCode、GC 分代年龄等。
5️⃣经过上述操作,一个新的对象已经产生,但是方法还没有执行,所有的字段都是零值。这时候需要执行方法(构造方法)把对象按照程序员的意愿进行初始化。类变量的初始化操作在类加载的初始化阶段方法完成。

三、分配内存

1️⃣分配内存有两种方式

  1. Java 堆内存是规整的(使用标记整理或带压缩的垃圾收集器),使用一个指针指向空闲位置,分配内存既将指针移动与分配大小相等的距离。
  2. 内存不是规整的(使用标记清除的垃圾收集器),虚拟机维护一个可用内存块列表,分配内存时从列表中找到一个足够大的内存空间划分给对象并更新可用内存列表。

无法找到足够的内存时会触发一次GC。

2️⃣分配内存时并发问题解决方案:

  1. 对分配内存空间的动作进行同步操作—采用 CAS 失败重试的方式保证更新操作的原子性。
  2. 每个线程在堆中预先分配一块小内存,称为线程本地分配缓存(Thread Local Allocation Buffer,TLAB),哪个线程要分配内存就在它的 TLAB 上分配,只有 TLAB 用完并分配新的 TLAB 时才需要同步锁定。通过-XX:+/-UseTLAB参数来设定。

四、创建对象指令重排序问题

A a = new A();new 一个对象的简单分解动作:

1️⃣分配对象的内存空间
2️⃣初始化对象
3️⃣设置引用指向分配的内存地址

其中2️⃣3️⃣两步会发生指令重排序,导致多线程时如果在初始化之前访问对象则会出现问题,单例模式的双重检测锁模式会存在这个问题。可以使用 volatile 来禁止指令重排序解决问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值