Jvm对象创建-JVM(六)

上篇文章说了jvisualvm工具查看年轻代老年代gc过程。

Jvisualvm&内存模型剖析-JVM(五)icon-default.png?t=N5K3https://blog.csdn.net/ke1ying/article/details/131524708
  • jvm创建对象

之前我们介绍了类的加载,这篇文章要介绍类的创建,过程主要是:

1、类的加载检查-----(是否已加载)

2、否的话,则加载类,是的话直接进入第三步

3、直接分配内存空间。

4、初始化

5、设置对象头

6、执行<init>方法

  1. 类加载检查

虚拟机遇到new指令,首先检测这个类是否在常量池中定位到类的符号引用,检测这个符号是否被加载、解析和初始化过,没有的话则必须先加载类。

  1. 分配内存

接下来虚拟机为新生对象分配内存,为对象分配内存等同于把一定大小的内存从java堆中划分出来。

但这会有两个问题:

  1. 如何划分内存。
  2. 并发情况下,在给对象A划分内存时候,指针还没来得及修改,在同一位置又给B划分内存。

划分内存分为两种,指针碰撞和空闲列表。

指针碰撞(Bump the Pointer):默认是指针碰撞,如果java内存是绝对规整的,所有用过内存都放在一边,空闲的放在另一边,中间放指针分解指示器,当分配内存后,就把指针移动到右侧。

空闲列表(Free List):如果java堆内存并不是规整的,已使用和空闲的内存相互交错,那么就没办法简单的指针碰撞,虚拟机会维护一个空闲列表,看哪些可用。

第一个划分问题策略已给出,那么如何解决并发问题呢。

有两种方式可以解决,CAS和本地线程分配缓冲TLAB。

CAS(compare and swap):比较和交换用cas配合失败重试机制来解决并发问题。

TLAB(thread local allocation buffer):把内存划分的动作按照线程划分在不同的空间,即每个线程在java堆中预分配一小块内存。通过-XX:+/UseTLAB参数设置虚拟机是否使用TLAB,而它的大小设置用的是-XX:TLABSize。

3、初始化

 

初始化就是前面说过的,当initDate在创建阶段,并不是直接把666赋值,而是先赋值0,String会先赋值null等。

  1. 设置对象头

 

前面初始化为0之后,虚拟机要对对象进行必要设置,例如哪个对象是哪个类实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息都存在对象的对象头Object Header中。

在hotSpot虚拟机中,对象内存中布局分为三块区域:对象头(header)、实例数据(instance data)、和对齐填充(Padding)

  1. 对象头(Object header)

Mark word:

有Mark word标记字段(32位占4字节,64位占8字节),自行运行时数据:哈希值,GC分代年龄,锁状态标志,线程持有锁,偏向线程ID,偏向时间戳。

从图可以看到分代年龄是4bit,最大是15,所以他的分代年龄不能超过15。

klass Pointer:

有klass Pointer类型指针(开启压缩占4字节,关闭压缩占8字节),类的元数据指针。

前面说了:一个对象new出来放在堆里,这时候对象头有一个klass Pointer指向方法区(元空间)的类的元素信息。(这里不是class,kclass是c++底层实现的)

数组:

数组长度(4个字节,只有数组才有)

 

 

打印对象信息我们需要引入这个maven包,如图所示。

Object大概16bytes,offset是偏移量。

前面两行数据存储的就是mark word。

第三行就是klass pointer,size显示8bytes

第四行是object alignment,对齐则就是填充padding,保证对象是8bytes的整数倍。

前面三行原本是12个字节,但因为对齐,所以多了4个字节,保证object是8的整数倍,这样计算可以保证计算机效率最高。

数组前面两行也是mark word。

第三行还是klass pointer。

第四行则是我们数组的长度,显示4个字节。

于是对齐就是0,因为已经是16个字节,不需要凑整为8的整数倍。

对象前面两行也是mark word。

第三行还是klass pointer。

第四行int类型默认4个字节

Byte类型b则是一个字节

字节会先内部对齐

之后则是name和object的对象默认都是4个字节

最后再次对齐成为8的倍数,28+4

指针压缩是什么?

前面说了klass没开启压缩是8个字节,开启是4个字节,那么我们刚打印的都是4个字节,为什么呢,因为jdk1.6之后默认都是指针压缩后的。

--XX:+UseCompressedOops(默认开启的)

--XX:-UseCompressedOops关闭命令

  1. 执行<init>方法

前面说了初始化会为0和null,这时候才会赋值代码里真实的值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

后端从入门到精通

你的鼓励是我最大的动力~

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

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

打赏作者

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

抵扣说明:

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

余额充值