深入理解Java虚拟机——对象的创建和内存布局

news2025/1/15 23:38:48

1.对象的创建

首先声明这一篇博客是在HotSpot虚拟机的前提之下记录的。主要参考书籍来源于周志明老师的《深入理解JVM虚拟机》。

在语言层面,创建对象仅仅是使用一个new关键字。但是从虚拟机的角度来看,创建一个对象一共有5个步骤:类加载检查分配内存初始化零值设置对象头执行<init>方法

类加载检查

当虚拟机遇到一条new指令的时候,首先去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用所代表的类是否已经被加载解析初始化过。如果没有就需要执行相应的类加载流程。

分配内存

类加载检查通过以后,虚拟机将对新生对象分配内存,对象所需要的内存大小在类加载完成以后即可完全确定。为对象分配内存实际上等同于将一块确定大小的内存块从Java堆中划分出来。

指针碰撞和空闲列表

分配内存的方式有两种,分别是指针碰撞空闲列表

指针碰撞:假设Java堆中内存是绝对规整的,所有使用过的内存都放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那分配内存就仅仅是把那个指针向空闲方向挪动一段与对象大小相等的距离。

空闲列表:如果Java堆内存是不规整的,已经被使用的内存与空闲的内存交错在一起,无法进行指针碰撞,虚拟机会会维护一个列表,记录哪块内存可用,哪块内存不可用,分配的时候找到一块足够大的空间划分给对象实例,并更新列表。

选择哪种分配方式由内存是否规整决定,内存是否规整又由所采用的垃圾收集器是否具有空间压缩整理的能力来决定。

如果我们使用Serial、ParNew收集器(这两种垃圾收集器采用了标记-整理算法),意味着具有空间压缩整理的能力,系统自然会选择简单高效的指针碰撞方法,而如果我们选择CMS这种基于标记清除的收集器,理论上就只能选择空闲列表方法来分配对象的内存。

理论上这么说是因为,在CMS垃圾收集器的内部,为了能在大多数情况下分配的更快,设计了一个叫做Allocation Buffer的分配缓冲区,在我们通过空闲列表拿到一大块可用内存以后,实际上对于这一大块可用内存,在它里面我们仍可以使用指针碰撞的方式来分配。

内存分配中的线程安全性

在JVM中,分配内存是一个非常频繁的行为,在并发的情况下是存在线程安全性问题的。

比如:正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针分配内存。

对此有两种解决方案:

  1. 对分配内存空间的操作进行同步处理。实际上虚拟机是采用CAS+失败重试的方式保证更新操作的原子性。
  2. 使用本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。为每个线程预先在堆内存中分配一小块内存,也就是TLAB,哪个线程需要分配线程就在哪个线程的TLAB中分配,只有本地缓冲区用完了分配新的缓存区时才需要同步锁定

可以通过虚拟机参数 -XX: +/-UseTLAB选择是否使用TLAB。默认情况下是开启的。

初始化零值

在HotSpot虚拟机中,对象在堆内存中的存储布局可以分为:对象头、实例数据、对其填充。这里只是提一下,后面还会说。

内存分配完成以后,虚拟机必须将分配到的内存空间(不包括对象头)都初始化为零值。如果使用了TLAB,那么这份工作在TLAB分配期间会顺便进行

这样可以保证在Java代码中,即使不赋初始值也可以使用。例如某个类的成员变量(int)即使不赋值,它的值就默认是0。不过要注意如果是某个局部变量(int)不赋值,那么他就没有默认值。

设置对象头

这个步骤中,虚拟机会对对象进行必要的设置。例如这个对象是哪个类的实例、如何才能找到类的元数据信息对象的哈希码(实际上只有真正调用对象的hashCode方法才会生成哈希码)、GC分代年龄等信息。

执行<init>()方法

从虚拟机的角度来看这个时候,一个新的对象已经产生了。但是从Java程序的角度来看这个时候,对象的创建才刚刚开始。

<init>()方法实际上对应的就是类的构造方法,只有执行构造方法之后,一个真正按照我们意愿的对象才算创建出来。

2.对象的内存布局

在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

对象头

对象头主要保存了两类信息:

  • 第一类是用于存储对象的运行时数据

    • 第一类是用于存储对象自身的运行时数据,如:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称它为 “Mark Word”。
    • 请添加图片描述
  • 第二类是类型指针,也就是指向该对象是哪个类的实例

    • 如果对象是一个 Java 数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通 Java 对象的元数据信息确定 Java 对象的大小,但是如果数组的长度是不确定的,将无法通过元数据中的信息推断出数组的大小。

实例数据

实例数据部分是对象真正存储的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的字段都必须记录起来。

对齐填充

对象的第三部分是对齐填充,这并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。

由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补全

3.对象的访问定位

Java程序会通过栈上的reference数据来操作堆上的具体对象。reference类型访问对象的方式是由虚拟机决定的,主要有句柄和直接指针两种。

句柄访问

如果使用句柄访问的话,Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的地址信息。
请添加图片描述

直接指针访问

如果使用直接指针访问的话,reference中存储的就是对象地址。

请添加图片描述

两种访问定位的方式各有各的好处:

  • 使用句柄,那么本地遍历表中的reference保存的是稳定的句柄地址,在对象被移动(垃圾收集器时移动对象是非常普遍的行为)时只会改变句柄中的地址信息,而reference本身并不需要被修改。

  • 使用句柄,那么本地遍历表中的reference保存的是稳定的句柄地址,在对象被移动(垃圾收集器时移动对象是非常普遍的行为)时只会改变句柄中的地址信息,而reference本身并不需要被修改。

  • 使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/491465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

排序大师:探秘C语言中神奇的qsort库函数

本篇文章中会详细讲解C语言中的qsort库函数。我准备分2个方面来讲&#xff1a; qsort如何使用。模拟实现qsort的效果。&#xff08;注意&#xff1a;只是用冒泡排序的思想实现类似的效果&#xff0c;实际qsort的底层采用的是快速排序的思想。&#xff09; 如何使用 先来看看q…

反调试与反反调试

参考文本 (190条消息) C 反反调试&#xff08;NtQueryInformationProcess&#xff09;_(-: LYSM :-)的博客-CSDN博客 Windows 平台反调试相关的技术方法总结—part 2 - 先知社区 C/C MinHook 库的使用技巧 - lyshark - 博客园 (cnblogs.com) (177条消息) C 反反调试&#x…

C结构简单而不失强大的表格

2023年了&#xff0c;想必已经不会有人对嵌入式开发中“数据结构&#xff08;Data Structure&#xff09;”的作用产生疑问了吧&#xff1f;无论你是否心存疑惑&#xff0c;本文都将给你一个完全不同的视角。 每每说起数据结构&#xff0c;很多人脑海里复现的一定是以下的内容&…

unity中用异步的whenAny,实现:当点击铲子任一部件,拾取整个铲子

一、铲子的组成 铲子包含很多部件组成&#xff0c;当拾取铲子的时候&#xff0c;只要点击铲子的任意一个部件就可以。 如图&#xff0c;点击【木柄】、【螺母】、【铁铲】都可以拾取该物体。 &#xff08;1&#xff09;打开高亮 &#xff08;2&#xff09;等待土铲被点击&…

为什么要通过API接口来获取数据

API接口&#xff08;应用编程接口 application/programming接口&#xff09;&#xff0c;准许应用程序通过定义的接口标准来访问另一个应用程序或服务的编程方式。简单来说&#xff0c;API就是两个软件或系统之间的通信语言或接口。 在当今的互联网时代&#xff0c;数据无处不…

Geospatial和Redis事务操作

一、Geospatial 1.简介 基于位置信息服务 (Location-Based Service,LBS) 的应用。 Redis3.2 版本后增加了对 GEO 类型的支持。主要来维护元素的经纬度。redis 基于这种类型&#xff0c;提供了经纬度设置、查询、范围查询、距离查询、经纬度hash等一些相关操作。 2.GEO底层结构…

DataEase 数据源插件分享 - 时序数据库 InfluxDB

前言 InfluxDB 是一个时序数据库&#xff0c;使用的是非标准的 SQL 语法&#xff0c;我使用 DataEase 的插件扩展机制开发了此数据源插件&#xff0c;在这里共享出来&#xff0c;想用的朋友可以下载安装使用。 插件包下载地址 https://north-dataease-1251506367.cos.ap-bei…

Centos 7.X WordPress博客网站详细教程 FTP/PHP/mysql/Apache环境构建

此教程适用于服务器系统为centos 7.x&#xff0c;php安装版本为7.4&#xff0c;mysql安装本部为5.7. 一、mysql安装 1.1 安装三个工具 yum install wget yum install vim yum install unzip 1.2 下载并安装msql 在线下载安装包&#xff1a; wget https://dev.mysql.com/g…

JZS-7/221静态可调延时中间继电器 JOSEF约瑟

JZS-7/2系列静态可调延时中间继电器品牌&#xff1a;JOSEF约瑟型号&#xff1a;JZS-7/2名称&#xff1a;静态可调延时中间继电器额定电压&#xff1a;48380V触点容量&#xff1a;10A/250V返回系数&#xff1a;≤15%延时范围&#xff1a;15ms3s15ms5s15ms10s JZS-7/2系列静态可…

SQL中使用的运算符号详解

文章目录 前言1. 算术运算符加法与减法运算符乘法与除法运算符求模&#xff08;求余&#xff09;运算符 2. 比较运算符1&#xff0e;等号运算符2&#xff0e;安全等于运算符3&#xff0e;不等于运算符4. 空运算符5. 非空运算符6. 最小值运算符7. 最大值运算符8. BETWEEN AND运算…

射频功率放大器(RF PA)线性化技术及分类介绍

基本概念 射频功率放大器(RF PA)是发射系统中的主要部分&#xff0c;其重要性不言而喻。在发射机的前级电路中&#xff0c;调制振荡电路所产生的射频信号功率很小&#xff0c;需要经过一系列的放大&#xff08;缓冲级、中间放大级、末级功率放大级&#xff09;获得足够的射频功…

Zabbix“专家坐诊”第190期问答汇总

问题一 Q&#xff1a;请问为啥用拓扑图监控交换机接口流量&#xff0c;获取不到数据&#xff0c;显示未知&#xff0c;键值也没错 &#xff0c;最新数据也能看到&#xff0c;是什么原因呢&#xff1f; A&#xff1a;把第一个值改成主机名。 问题二 Q&#xff1a;请问下zabbi…

如何进行AI换脸,AI换脸从 “0“ 到 “1” 详细教程 ——从配置环境开始

后续文章读起来可能会影响观看可以前往鄙人博客查看&#xff1a;http://www.anyuer.club/?id199 前言&#xff1a; 本人吃计算机这口饭的&#xff0c;说实话AI换脸很火的时候自己却没碰&#xff0c;挺吃亏的&#xff0c;最近时间比较充裕&#xff0c;整理了一下AI换脸的一个简…

Pyecharts 输出到 html 白屏?终极解决方案来了。

问题起因 公司内部网络&#xff0c;想要做个饼图输出到 html 。 找了教程&#xff1a;https://pyecharts.org/#/zh-cn/quickstart 我看教程写得这么规范&#xff0c;直接 CtrlC&#xff0c;CtrlV&#xff0c;百度来的代码怎么可能会有问题嘛&#xff01; 人生处处有惊喜。 样…

SpringBoot中策略模式+工厂模式业务实例(接口传参-枚举类查询策略映射关系-执行不同策略)规避大量if-else

场景 设计模式-策略模式在Java中的使用示例&#xff1a; 设计模式-策略模式在Java中的使用示例_java 策略模式示例_霸道流氓气质的博客-CSDN博客 上面讲了策略模式在Java中的使用示例。 下面看一个在SpringBoot中的实际使用示例。 业务场景: 有多个煤矿&#xff0c;信号灯…

推荐一些非常好用的DNS服务器

推荐一些非常好用的DNS服务器 1、114公共DNS服务器 1&#xff09; 老牌的114DNS&#xff0c;全国三网通用高速&#xff0c;纯净无劫持无需再忍受被强扭去看广告或粗俗网站之痛苦 DNS地址为&#xff1a;114.114.114.114 和 114.114.115.115 2&#xff09;拦截 钓鱼病毒木马网…

三顾茅庐,七面阿里,终拿25k*16offer,我的面试历程

写在片头&#xff1a;声明&#xff0c;勿杠 首先简单说一下&#xff0c;这三次面试阿里并不是一次性去面的&#xff0c;实际上第一次面试时候还在大四&#xff0c;找的实习岗&#xff0c;不太清楚是什么部门&#xff0c;别问我为什么还记得面试题&#xff0c;有记录和复盘的习…

DX算法还原

早在之前作者就写过一篇关于顶象的滑块验证&#xff0c;潦潦草草几句话就带过了。 出于互相学习的想法&#xff0c;给了一个大学生&#xff0c;奈何不讲武德把源码甩群里了&#xff0c;虽然在大佬们眼里不难&#xff0c; 不过拿着别人的东西乱传还是不太好。自认倒霉&#xf…

基于max30102的物联网病房监测系统(传感驱动和数据处理)

目录 一、实物展示 二、主体介绍 三、MAX30102的驱动 四、MAX30102的数据处理 奋斗一个星期&#xff0c;每个引脚都是扒皮焊接然后再把皮包回去的。这几天吸的垃圾气体感觉要少活两年。 一、实物展示 这次吸取上次教训&#xff0c;把线捆起来好多了 二、主体介绍 用的传感…

Python进阶篇

大家好&#xff0c;我是易安&#xff01;今天我们继续Python的学习&#xff0c;内容稍微有些多&#xff0c;不过我会尽可能举一些例子让你理解。 对象比较与拷贝 在前面的学习中&#xff0c;我们其实已经接触到了很多 Python对象比较和复制的例子&#xff0c;比如下面这个&…