JVM(二):HotSpot虚拟机对象探秘

来源:互联网 发布:淘宝比较真的法国代购 编辑:程序博客网 时间:2024/05/19 17:25

首先我想在这里声明一下,我从事Java软件开发四年时间,一直没有时间去写自己的博客,这次写的博客大多数都是我自己学习的读书笔记,没有什么原创与不原创,俗话说林子大了什么鸟都有,同样的博客多了什么内容都有,肯定会有一些雷同之处,当然,我才疏学浅,博文肯定也有一些不正确的地方,各位大牛请不要见笑,也请各位大牛及时之处不足之处,以帮助我在软件开发的职业道路中少走一些弯路,谢谢。


1. 对象的创建

当虚拟机遇到一个new指令的时候,首先会去检查这个指令的参数,看这个参数是否能够在常量池中定位到一个类的符号引用,并且检查这个类是否被加载,解析和初始化,如果没有的话需要先执行类加载。

检查通过后,给新生对象分配内存,分配内存的方式有两种,指针碰撞和空闲列表,如果java堆是规整的,会采用指针碰撞,如果不规整,则会采用空闲列表,java堆是否规整取决于虚拟机采用的垃圾收集器是否带有压缩整理功能。

理解了内存分配的方式之后,我们还需要考虑一个问题,就是内存分配的线程安全问题。

不管采用指针碰撞还是空闲列表,在并发的情况下都不是线程安全的,这个问题虚拟机也采用了两种方式进行解决,一种是对分配内存空间的动作进行同步处理,一般是采用CAS和失败重试的方式保证更新操作的原子性,另一种就是采用TLAB,也就是本地线程分配缓冲。

分配完成后,虚拟机会把分配到的内存空间初始化为零值,这个初始化不包括对象头。

再接下来,就是虚拟机对对象进行必要的设置,比如说这个对象是那个类的实例,对象的哈希码,如果能找到类的元数据信息,对象的GC分代年龄信息等,这些东西都存在对象头中。

从虚拟机角度上讲,这个时候一个对象就产生了,从程序员的角度上讲,执行new指令后还需要执行init方法,按照程序员的意愿初始化出来,这样一个真正可用的对象才真正的产生。


2.对象的内存布局

对象在内存中的布局分为对象头,实例数据,对齐填充三部分。

对象头的第一部分存储了对象自身的运行时数据,比如哈希码,分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等。

对象头的第二部分存储了类型指针,类型指针指向了方法区,对应存储的是对象类型数据。

实例数据中是对象真正储存的有效内容,也就是代码里面类中定义个各个类型的字段数据。

对齐填充可有可无,只是为了补齐8的倍数,因为对象在内存中占有的大小正好是8的倍数。


3.对象的访问定位

对象的访问定位有两种方式,句柄访问和指针访问。

不同点在于

指针访问的方式,存储引用的栈帧中,存储了一个指针,指针的地址是堆中的实例数据,实例数据中包含了对象类型数据的指针,指向了方法区中的对象类型的数据。

句柄访问的方式,储存引用的栈帧中,存储了一个指针,指针的地址是堆中划分出来的句柄池中的一块内存,这块儿内存存储了两个指针,一个是指向堆的另一部分实例池,实例池中存储的是实例数据,另一个指针指向的是方法区中的对象类型数据。

指针的方式会更快,减少了一次句柄池地址的查找。





原创粉丝点击