烧点脑子使劲看--对象详细讲解

简介: 当Java虚拟机遇到一条new字节码指令时,首先会检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用所代表的类是否已经被加载,如果没有,就必须先将就该类加载到内存中,具体过程见:

一、对象的创建


当Java虚拟机遇到一条new字节码指令时,首先会检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用所代表的类是否已经被加载,如果没有,就必须先将就该类加载到内存中,具体过程见:类的加载过程


在类加载检查通过后,虚拟机就会为这个对象分配内存,内存分配的方式主要有两种:

  • 指针碰撞:如果JVM堆内存是绝对规整的,所有已经使用的内存都放在一边,空闲的内存都放在另一边,中间用一个指针指向分界点,那么只需要将指针往后移动新对象的大小同等距离即可。
  • 空闲列表:如果堆内存不是规整的,那么就无法使用“指针碰撞”的方式来为新对象分配内存,虚拟机就需要维护一张列表,用于记录堆内存上哪一块空间是空闲的,在这个列表中选取一块足够大的内存空间为新对象分配内存,并更新列表。

具体使用哪种内存分配方式是由使用的垃圾收集器是否具有空间压缩整理的能力决定的,使用SerialParNew这种带压缩整理过程的垃圾收集器时,采用的分配算法是指针碰撞。而如果使用CMS这类基于标记-清除算法的垃圾收集器时,将采用空闲列表来分配内存

由于对象的创建在虚拟机中是非常频繁的行为,在并发情况下并不是线程安全的,可能出现为A对象分配内存空间,指针还没来得及修改,对象B又使用了同一块内存空间。解决这个问题又两种可选方案:

  • 采用同步的方式分配内存空间,在Java虚拟机中采用CAS加上失败重试的方式来保证更新操作的原子性。
  • 另一种方案是将内存分配动作按照线程划分在不同的空间中进行,即每个线程再Java堆中预先分配一块小内存,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)。哪个线程需要分配内存,就在哪个线程的本地线程分配缓冲区中分配,只用当本地线程分配缓冲中的空间不够为新对象分配空间了,才需要采用同步的方式分配空间。虚拟机是否使用本地线程分配缓冲,可以通过-XX:+/-UseTLAB参数来设置。

在内存分配完成后,虚拟机还需要对象的对象头进行必要的设置,例如GC分代年龄、这个对象属于哪个类的实例、如何才能找到类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到调用hashCode()方法才计算)等。

执行完上面的操作后,虚拟机还需要执行Class文件中的方法,也就是我们在代码中写的构造函数。


二、对象的内存布局


在HotSpot虚拟机中,对象在堆内存中的存储结构可以分为三个部分:

  • 对象头(Head)

对象头包括两类信息。第一类是用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁;偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机中分别为32个比特和64个比特,称为“Mark Word”。

网络异常,图片无法展示
|

对象头的另一部分是类型指针(Klass Word),即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是属于哪个类的实例,如果对象是一个Java数组,那么对象头中还必须有一块用于记录数组长度。

  • 实例数据(Instance Data)

实例数据是对象真正储存的有效信息,是我们在代码中定义的各种类型的字段内容,包括从父类继承下来的。这部分的存储顺序会受到虚拟机分配策略参数(-XX:FieldsAllocationStyle)和字段在源码中定义的顺序的影响。HotSpot虚拟机默认的分配顺序是:longs/doublesintsshorts/charsbytes/booleansoops(Ordinary Object Pointers, OOPs),父类定义的字段会出现子类前面。

  • 对齐填充(Padding)

对齐填充不是必须的部分,也没有什么特别的含义,仅仅是为了将对象的大小凑到8字节的倍数。

目录
相关文章
|
4月前
|
存储 Java
八股day03_方法
八股day03_方法
|
7月前
|
存储 编译器 C语言
【C++初阶】第二站:类与对象(上) -- 上部分
【C++初阶】第二站:类与对象(上) -- 上部分
|
7月前
|
存储 编译器 C语言
【C++初阶】第二站:类与对象(上) -- 下部分
【C++初阶】第二站:类与对象(上) -- 下部分
|
前端开发 JavaScript
用Transition组件犯迷糊?看我这篇给你安排的明明白白的
transition组件作为Vue的内置组件,可以用来实现组件的过渡效果。在Vue中,过渡效果是通过CSS来实现的,所以过渡不是如何使用组件,而是如何写CSS。
145 0
用Transition组件犯迷糊?看我这篇给你安排的明明白白的
|
JavaScript 前端开发 C语言
带你读书之“红宝书”:第十章 函数③
带你读书之“红宝书”:第十章 函数③
108 0
带你读书之“红宝书”:第十章 函数③
|
存储 JavaScript 前端开发
带你读书之“红宝书”:第十章 函数②
带你读书之“红宝书”:第十章 函数②
108 0
带你读书之“红宝书”:第十章 函数②
|
前端开发
带你读书之“红宝书”:第五章 基本引用类型③
带你读书之“红宝书”:第五章 基本引用类型③
90 0
带你读书之“红宝书”:第五章 基本引用类型③
|
安全 Java 编译器
学妹不懂Java泛型,非让我写一篇给她看看(有图为证)
笔者有个学妹就遇到了相同的境遇,学弟被泛型搞得头晕目眩,搞不懂泛型是个啥玩意。天天用的泛型也不知道啥玩意(她可能都不知道她有没有用泛型)。立图为证!当然,笔者深度还欠缺,如果错误还请指正!
143 0
学妹不懂Java泛型,非让我写一篇给她看看(有图为证)
|
消息中间件 Kubernetes Cloud Native
记一次内部分享——瞎扯淡
记一次内部分享——瞎扯淡
记一次内部分享——瞎扯淡
|
JavaScript 前端开发 C++
每日一题:谈谈this对象的理解
每日一题:谈谈this对象的理解
103 0