dccmmtop / notebook

个人博客记录
0 stars 0 forks source link

jvm中对象的创建过程 #72

Open dccmmtop opened 2 years ago

dccmmtop commented 2 years ago

1. 类加载检查

虚拟机遇到一条 new 指令时,首先检查这个指令的参数能否在常量池中找到一个类符号引用,并且检查这个符号引用代表的类是否已经被加载,解析,初始化过。如果没有必须先执行类的加载初始化过程。

2. 分配内存

在类加载检查通过后,接着就可以为新生对象划分内存了,对象占用内存的大小在类加载后就可以完全确定。为对象分配内存空间就相当于把一块确定大小的内存从java堆中划分出来

如何划分内存呢

内存是如何划分的呢?高并发的场景下如何保证同一块空间不会分给两个对象的呢?

指针碰撞 Bump the pointer (默认)

如果虚拟机堆中内存是绝对规整的,用过和没用过的各占一块完整的内存,中间放着一个指针作为分界点的指示器,在进行内存分配时,只需把指针向空闲区域移动一段距离,以放下新对象。

空闲列表 Free List

如果虚拟机堆中的内存不是规整的,用过的和没有用过的互相交错,就没有办法使用指针碰撞的方法;了。虚拟机必须维护一个列表,来记录队中有哪些区域是空闲的。在分配内存的时候找到一块足够大的空间分配给对象,并更新列表记录

解决并发

CAS (compare and swap)

本地线程分配缓冲(Thread Local Allocation Buffer TLAB)

每个线程预先在jvm堆中分配一块内存空间,线程声明周期内的对象分配都在这实现分配的空间中进行

-XX: +UseTLAB 设置虚拟机使用TALAB -XX:-UseTLAB 不使用

-XX:TLABSize 指定TALB 的大小

3. 初始化

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,(不包括对象头),这一步操作保证了对象的实例字段在java代码中可以不赋初始值就能直接使用,如果是TLAB 方式,这一步骤将提前到TLAB分配时进行

4. 设置对象头

初始零值之后,要虚拟机要对对象进行一些必要的设置,比如,这个对象是哪个类的实例,如何才能找到类的一些元信息,对象的哈希码,对象的GC分代年龄信息,这些信息存放在对象头 Object-Header 中。

在HotSpot虚拟机中,对象在内存中分布可以又3各部分祖成,对象头,实例数据,对齐填充。

对象头

HotSpot 虚拟机的对象头包含两个部分

4.1 自身的运行数据

实例数据

存放对象的实际数据

对齐填充

不是必然存在,也没有特别的含义,只是起着占位符的作用,那么为什么会有这一部分的数据呢?

这就需要了解一些计算机组成原理的知识了。

CPU 位数

我们经常使用的CPU 有32 位和64 位

32位架构的CPU数据总线宽度是32位,每次可以传输32位数据,可以计算4个字节。

64位架构的CPU数据总线宽度是64位,每次可以传输64位数据,可以计算8个字节。

CPU 寻址

CPU 在工作时,从内存中的某个地址取数据,然后进行计算。数据最小的保存单位时 位 (bit), 为了减少CPU与内存通信的次数,规定CPU一次从内存中取8位数据,也就是1字节。内存中每一位(bit) 都有一个唯一的地址。

对于32位的CPU,在向内存取数据时,所能描述的最大地址是第 2^32 字节(CPU一次最少取1个字节,不是1位),2^32字节 约等于 4G, 内存再大,CPU 也无法访问到,所以32位的CPU 最大支持约4G的内存了。 同理,64 位的CPU 支持 2^64 字节内存,等于 17179869184 Tb

Java对象最小占用空间

由于64 位的CPU一次能处理 64 位指令,也就是8个字节的数据,JVM 也遵循这个规则,让对象的内存占用是8字节的的整数倍,CPU 处理更高效。那么如何保证 java 对象占用的空间永远是8字节的整数倍呢? 答案就是内存对齐,不足8字节的整数倍,末尾补充空白占用符。

Java对象的指针压缩

上述的内存对齐,相当于把对象的占用的空间扩大了,与之对应的,JVM 还有内存压缩机制:

指针压缩的好处
  1. 在64位平台的HotSpot中使用32位指针,内存使用会多出1.5倍左右,使用较大指针在主内存和缓存之间移动数据,占用较大宽带,同时GC也会承受较大压力
  2. 为了减少64位平台下内存的消耗,启用指针压缩功能
  3. 在jvm中,32位地址最大支持4G内存(2的32次方),可以通过对对象指针的压缩编码、解码方式进行优化,使得jvm只用32位地址就可以支持更大的内存配置(小于等于32G)
    指针压缩的注意点
  4. 堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间
  5. 堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址,这就会出现1的问题,所以堆内存不要大于32G为好

4.2 类型指针

对象指向他类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

5. 执行\<init>方法

执行\<init> 方法,就是按照程序员的意愿进行初始化,对应到语言层面上就是为属性赋值,和执行构造方法