JVM之对象创建流程及对象内存布局

简介: JVM之对象创建流程及对象内存布局

JVM之内存区域及对象创建流程

对象创建流程

当JAVA虚拟机碰到new字节码指令时,首先会去常量池中查找是否有对应的类名(也就是去查找是否有对应的符号引用),然后去检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有会先进行类加载过程。

当类加载后,虚拟机将会为其分配内存,为其分配的内存大小是可知的,下面的内存布局将会讲解为什么是可知的。

分配内存

分配内存这时候有两种情况:

1.假如堆里面的内存是整齐的,用过的在一边,没有用过的内存放在另外一边(后期配图),这个时候中间有个指针来作为两边内存的界限,当内存分配时,指针移动对象内存大小对应的距离即可,这种叫指针碰撞

2.堆里面的内存空间不是规整的,这就需要记录下来哪些内存是可用的,哪些内存是已经被占用了的。这种方式就叫做空闲列表:将内存中空闲内存块记录到列表里面。当分配对象时,直接从空闲列表里面进行取出对应大小的内存块即可。

这两种情况又是根据采用的垃圾收集器是否带有空间压缩整理的能力划分,如果垃圾收集器已经具备了空间压缩整理的能力那么他的内存空间就是被整理好的,直接使用指针碰撞就好;但是如果是基于清除算法决定的垃圾回收器时,就只能用复杂的空闲列表来分配内存。

并发解决方法

但是如果发生并发的话,可能在分配一个对象空间的时候又碰到另外一个线程也在分配空间,这个时候就会出现问题,解决方式有两种:

1.通过CAS进行同步处理,基于失败重试的原则;

2.将堆里面的空间进行按线程分配,每个线程在队中都会有块内存,当线程分配内存时,直接分配到自己线程的那块内存当中,当那小块内存用完时,在进行CAS同步申请新的内存,这种小块内存叫做本地线程分配缓存(TLAB)

设置初始值

分配完内存之后需要给这部分内存设置零值,不包括对象头。
当通过TLAB分配内存时,其实在分配内存的时候就可以设置零值,不需要等到分配完在设置,因为这部分内存区域是已知的不会出现分配时产生并发的问题

在程序中可能会出现的问题

解释:

这步操作也就是说当对象分配到内存后就可以直接使用里面的字段,但是这个是初始值,如果说当我分配完内存后直接使用这个字段的话程序肯定会出问题(因为CPU是乱序执行的,当两个操作互不关联时,一个操作耗时一个操作不耗时,这时候CPU会进行优化让不耗时的先运行。而且一个创建对象的过程需要多行字节码来完成,所以可能会出现重排序的问题,但是这个概率特别低)这时候就需要用volatile关键字来保证有序性。

其本身是通过在JVM平台上面的Load,Store两个读写屏障组合来保证的,对应于intel的X86来说是基于MESI协议来保证的。

其实JVM平台规定了一些不能乱序执行的原则:HappenBefore原则,里面就规定了volitaile关键字

设置对象头

当对象中的字段设置为对应的默认值(零值)时,需要设置对象头里面的数据,这部分数据包括两部分:

对象头数据结构

1.对象自身运行时的数据

比如:

哈希码(延迟到真正调用hashcode()方法时才生成)

锁状态标志

线程持有锁

偏向锁的线程ID

偏向时间戳

对象分代年龄

在未开启压缩指针的情况下,根据32位虚拟机和64位虚拟机不同,这部分数据的总大小分别是32个比特和64个比特。

这部分数据叫做“Mark Word”,由于对象运行时存储的数据很多,所以Mark Word是一个动态的数据结构,有些数据其实根本用不到所以某些数据其实是没有必要立马就存储的。

32位的虚拟机中,MarkWord是32个比特,其中哈希码占用25个比特,分代年龄占用4个,锁标志位占用两个,剩下的另外一个比特固定为0。

2.类型指针

指向类的元数据信息,通过这个指针来确定该对象属于哪个类的实例。

(不是所有的虚拟机都必须在对象数据上设置类型指针)

当对象是数组。。。。

如果对象是数组,在对象头中还会记录数组长度,普通JAVA对象可以通过找到类的元数据信息确定JAVA对象的大小,但是数组长度是不能通过类的元数据信息推导出来的,所以需要在对象头中设置数组长度

Class文件的<.init>

当设置完字段的默认值和对象头的数据后,这个时候该调用Class对象的<.init>方法了即构造函数。

对象的内存布局

当了解完前面的对象创建流程时,相信对于对象在堆中的内存布局也已经有两大概的轮廓了,接下来进行总结:

分为三部分:对象头,实例数据,对齐填充

1.对象头前面已经详细讲过了,就不在阐述了

2.实例数据:

记录父类和当前类中定义的字段,存储的顺序默认是:

long/doubles , ints , shorts/chars , bytes/booleans , oops。默认顺序遵从的原则是相同宽度的字段分配到一起,接着父类定义的变量在子类定义的变量的签名。

3.对齐填充:不是必然的

占位符。

由于HotSpot虚拟机自动内存管理系统要求对象的起始地址必须是8字节的整数倍,也就是对象的大小都必须是8的倍数。

对象头刚刚说了无非是32比特或者64比特默认就是八字节的,所以当实例数据满足八的倍数时,就不需要占位符,这部分数据也就没有;如果不满足八的倍数,将添加占位符使整个对象大小为八的倍数。


目录
相关文章
|
19天前
|
存储 算法 Java
散列表的数据结构以及对象在JVM堆中的存储过程
本文介绍了散列表的基本概念及其在JVM中的应用,详细讲解了散列表的结构、对象存储过程、Hashtable的扩容机制及与HashMap的区别。通过实例和图解,帮助读者理解散列表的工作原理和优化策略。
29 1
散列表的数据结构以及对象在JVM堆中的存储过程
|
15天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
43 4
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
68 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
Java 测试技术 Android开发
让星星⭐月亮告诉你,强软弱虚引用类型对象在内存足够和内存不足的情况下,面对System.gc()时,被回收情况如何?
本文介绍了Java中四种引用类型(强引用、软引用、弱引用、虚引用)的特点及行为,并通过示例代码展示了在内存充足和不足情况下这些引用类型的不同表现。文中提供了详细的测试方法和步骤,帮助理解不同引用类型在垃圾回收机制中的作用。测试环境为Eclipse + JDK1.8,需配置JVM运行参数以限制内存使用。
32 2
|
1月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
56 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
385 0
|
29天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
60 1
|
1月前
|
存储
共用体在内存中如何存储数据
共用体(Union)在内存中为所有成员分配同一段内存空间,大小等于最大成员所需的空间。这意味着所有成员共享同一块内存,但同一时间只能存储其中一个成员的数据,无法同时保存多个成员的值。
|
1月前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
1月前
|
存储 编译器
数据在内存中的存储
数据在内存中的存储
42 4