【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问

news2024/11/25 3:51:51

HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问

一、对象的创建

Step1:类加载检查

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

(注意:
Java虚拟机中类的符号引用和方法的符号引用都存放在运行时常量池中,运行时常量池是方法区的一部分区域。运行时常量池是在类加载后在内存中分配的一块区域,用于存放编译期生成的各种字面量和符号引用。在类装载后,JVM会从class文件的常量池中把符号引用存放到运行时常量池中,并进行解析,生成直接引用。)

Step2:分配内存

类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来
分配方式有 “指针碰撞”“空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定(即Java堆内存是否连续),而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

问题一:内存分配的两种方式

指针碰撞:

  • 适用场合:堆内存规整(即内存连续,没有内存碎片)的情况下。
  • 原理:用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。
  • 使用该分配方式的 GC 收集器:Serial, ParNew

空闲列表

  • 适用场合:堆内存不规整的情况下(内存不连续)。
  • 原理:虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找一块儿足够大的内存块儿来划分给对象实例,最后更新列表记录。
  • 使用该分配方式的 GC 收集器:CMS

问题二:内存分配并发问题

在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:

  • CAS+失败重试: CAS是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用
    CAS 配上失败重试的方式保证更新操作的原子性。
  • TLAB(Thread Local Allocation Buffer 线程本地缓冲区): 为每一个线程预先在 Eden 区(堆的一块内存区域)分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配
    所以JVM虚拟机给对象分配内存是通过TLAB(线程本地缓冲区技术)和CAS原子操作来保证线程安全的。

Step3:初始化零值

内存分配完成后,Java虚拟机会将分配到内存空间的实例变量都初始化为零值或者空(不包括对象头,即类对象下的属性变量初始化为零或者空),这一步操作保证了对象实例字段在Java代码中可以不赋初始值就直接使用

Step4:设置对象头

初始化零值完成后,虚拟机要对对象进行必要的设置,如:对象所属的类的元数据信息、对象锁的状态、对象的哈希码、对象的GC相关信息等,这些信息存放在对象头中,即需要设置对象头(对象头会有不同的设置方式)。

问题:什么是对象头,举例说明一下呢?

对象头(Object Header)是Java对象实例在内存中的固定长度的数据结构,由Java虚拟机负责分配和管理。它包含了对象实例的运行时信息,如对象所属的类的元数据信息对象的锁状态GC相关信息等。通常情况下,对象头的长度在32位和64位虚拟机中是不同的。在32位虚拟机中,对象头占用8个字节,在64位虚拟机中,对象头占用12个或16个字节(具体长度取决于虚拟机实现)。

举个例子如下:
假设有一个名为Person的类,它的定义如下:

public class Person {
    private int id;
    private String name;
    private int age;
    
    // ...其它类成员省略...
}

当我们创建一个Person对象时,Java虚拟机会为该对象分配一块内存空间,其中包括对象头和实例变量。假设创建一个Person对象p1,它的内存结构大概如下(这里以32位虚拟机为例):

[对象头 8B] [id变量 4B] [name变量 4B] [age变量 4B] [ padding 4B ]

其中,对象头8个字节的内容包含了Person类的元数据信息、对象的锁状态、GC相关信息等。实例变量包括了id、name和age三个变量,它们的值在初始化零值操作后会被赋为0或null,具体取决于变量的类型。

在后续的操作中,我们可以使用该对象的实例变量和方法,也可以使用Java虚拟机提供的API对对象头进行操作,以实现各种功能。例如,我们可以使用synchronized语句块来实现多线程同步,这需要Java虚拟机在对象头中记录锁状态,这样就可以对对象进行加锁和解锁。

Step5:执行init方法

上面工作完成后,从虚拟机的角度来看,一个新的对象已经产生了。
但是从java程序的角度出发,对象的创建才开始,init 方法(构造方法)还没有执行,所有字段都还为零或空。
所以一般来说,执行new指令后会接着执行init 方法,将对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

二、对象的内存分布

在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:对象头实例数据对齐填充

  • Hotspot 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
  • 实例数据部分:存储的是对象真正的有效信息,即在程序中定义的各种类型的字段内容。
  • 对齐填充部分对齐填充部分不是必然存在的,也没有什么特殊的含义,仅仅起到占位作用。因为Hotspot虚拟机要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。而对象头正好是8字节的倍数(1倍或2倍),因此当对象的实例数据部分没有对齐时就需要通过对齐填充来补全。

三、、对象的访问定位

对象的实例会存放在堆中,如何访问到,那么如何访问到对象呢?

java程序通过栈上的reference数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式:使用句柄直接指针

  • (一)句柄
    如果使用句柄方式的话,那么java中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据对象类型数据各自的具体地址信息。
    在这里插入图片描述
    优点:
    使用句柄方式访问对象最大的好处是reference中存储的是稳定的句柄地址,在对象被移动时(垃圾回收会造成堆中对象地址变动),只需要同步变动句柄池中对象实例数据指针即可。
    缺点
    访问对象需要多访问一次句柄池,多了一次指针定位的开销。

  • (二)直接指针
    使用直接指针方式访问,reference中存储的就是堆中的对象地址,相比与使用句柄访问节省了一次指针定位的时间开销。而我们的Java应用中,访问对象是非常频繁的,所以都使用直接指针的访问方式一步到位。HotSpot虚拟机主要使用的就是这种方式来进行对象访问。
    在这里插入图片描述

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

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

相关文章

【深度学习Week2】卷积神经网络

卷积神经网络 Convolutional Neural Networks,CNN 【第一部分:代码练习】1.MNIST 数据集分类2.CIFAR10 数据集分类3.使用 VGG16 对 CIFAR10 分类 【第二部分:问题总结】 【第一部分:代码练习】 1.MNIST 数据集分类 1.1 加载数据…

STM32入门学习之USART串口通信:

1.串口通信简介:通用异步收发传输器UART(Universal Asynchronous Receiver/Transmitter)是负责处理数据总线和串口之间的串/并通信的设备。UART通信规定了数据帧的格式:起始位、数据位、校验位、停止位等。UART异步通信只需要通信双方设置好数据帧的格式…

html2Canvas+JsPDF 导出pdf 无法显示网络图片

html2CanvasJsPDF 导出pdf 问题:类似于下面着这种网络图片使用img导出的时候是空白的 https://gimg3.baidu.com/search/srchttp%3A%2F%2Fpics4.baidu.com%2Ffeed%2F7e3e6709c93d70cf827fb2fda054500cb8a12bc9.jpeg%40f_auto%3Ftoken%3Dd97d3f0fd06e680e592584f8c7a2…

Devart UniDAC Crack

Devart UniDAC Crack 通用数据访问组件(UniDAC)是一个强大的非可视化跨数据库数据访问组件库,适用于Delphi、Delphi for.NET、CBuilder和Lazarus(Free Pascal)。我们将长期成功开发的经验结合到一个产品中,提供对流行数据库服务器的统一访问,…

Sublime Text 4 激活教程(Windows+Mac)

下载安装 官网 https://www.sublimetext.com 点击跳转 2023.7.21 版本为4143 Windows激活方式 一、激活License方式 入口在菜单栏中"Help” -> “Enter License” 注意格式,可能会过期失效,失效就用方式二 Mifeng User Single User License E…

SUSE宣布推出免费RHEL分叉以保留企业级Linux的选择权

导读在Red Hat宣布将限制AlmaLinuxOS或Rocky Linux等社区发行版对其公共仓库的访问后,最近Red Hat与IBM之间发生了一些争论,有鉴于此,SUSE今天宣布计划为RHEL和CentOS用户提供一个免费的替代方案。 SUSE已经开发了SUSE Linux Enterprise (SLE…

【数据挖掘】PCA/LDA/ICA:A成分分析算法比较

一、说明 在深入研究和比较算法之前,让我们独立回顾一下它们。请注意,本文的目的不是深入解释每种算法,而是比较它们的目标和结果。 如果您想了解更多关于PCA和ZCA之间的区别,请查看我之前基于numpy的帖子: PCA 美白与…

Fatdog64 Linux 814发布

导读Fatdog64 Linux是一个小型、桌面、64位的Linux发行版。 最初是作为Puppy Linux的衍生品,并增加了一些应用程序。该项目最新的版本,Fatdog64 814,是8xx系列的最后一个版本,未来的版本将转向9xx基础。 尽管它是该系列的最后一个…

红黑树概念

这里写目录标题 红黑树概念红黑树的性质红黑树节点的定义红黑树的插入 红黑树概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制&…

Docker Compose 解析:定义和管理多容器应用,从多角度探索其优势和应用场景

🌷🍁 博主 libin9iOak带您 Go to New World.✨🍁 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~&#x1f33…

【代码随想录 | Leetcode | 第十天】哈希表 | 三数之和 | 四数之和

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏,今天将为大家带来哈希法~三数之和 | 四数之和的分享✨ 目录 前言15. 三数之和18. 四数之和总结 15. 三数之和 ✨题目链接点这里 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], num…

flask 页面新增文件,存在重复文件时,返回错误消息

(40条消息) flask 读取文件夹文件,展示在页面,可以通过勾选删除_U盘失踪了的博客-CSDN博客 项目结构 这是一个基本的Flask应用程序,主要有两个路由,一个是index,用于显示所有存在的文件以及用于删除已选的文件&#…

C# SolidWorks 二次开发 -从零开始创建一个插件(2)

上一篇我详细讲解了如何创建一个插件,但是无界面无按钮,这种插件适合配合事件偷偷的在后台做点什么事情。今天这篇讲一下如何增加一些按钮到工具栏、菜单上去。 先告诉大家这个东西注册表在哪,因为solidworks在这方面做的不太好,…

七大排序算法和计数排序

文章目录 一、直接插入排序二、希尔排序三、直接选择排序四、堆排序五、冒泡排序六、快速排序6.1递归实现快速排序6.2非递归实现快速排序 七、归并排序7.1递归实现归并排序7.2非递归实现归并排序 八、计数排序 以下排序以从小到大排序为例 一、直接插入排序 时间复杂度&#x…

如何从gitee上下载项目并把它在本地运行起来

有时候我们会想到在gitee上下载下来项目,那么怎么把项目下载到本地并跑起来呢? 第一步:在git上找到你想要克隆下来的项目,按照如下操作复制项目地址连接,如下图: 以上可以选择HTTPS和SSH两种形式。 第二步…

在SPringBoot中整合Mybatis-plus以及mybatis-puls的基本使用

创建SPringBoot项目 1.选择创建项目 2.创建SPringBoot项目 3.选择SPringBoot的版本和依赖 4.导入mysql,druid,mybatis-plus和lombok的依赖,导入后记得更新依赖 <dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId…

Mybatis单元测试,不使用spring

平时开发过程中需要对mybatis的Mapper类做单元测试&#xff0c;主要是验证语法是否正确&#xff0c;尤其是一些复杂的动态sql&#xff0c;一般项目都集成了spring或springboot&#xff0c;当项比较大时&#xff0c;每次单元测试启动相当慢&#xff0c;可能需要好几分钟&#xf…

Mac 四大常用清理软件推荐,软件特色下载教程横向评测

Mac 一般来说基本是不会中毒的&#xff0c;而且像 现在的 windows 也是很少中毒&#xff0c;但我们可能还是需要一款杀毒清理软件&#xff0c;主要是为了清理垃圾&#xff0c;统一查看并管理软件开机自启、权限信息等&#xff0c;统一卸载清理等功能&#xff0c;另外我们可能还…

【机器学习】PyTorch手动实现Logistic回归算法

参考地址&#xff1a;点击打开 计算较为繁琐&#xff0c;需要用到sigmoid函数和梯度下降算法&#xff0c;步骤主要如下&#xff1a; 二项分布概率公式表示最大似然估计和对数化计算求道带入梯度下降算法计算和优化 代码&#xff1a; import numpy as np import matplotlib.py…

05.计算机网络——TCP协议

文章目录 TCP协议段格式TCP交付过程TCP解包过程确认应答机制\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kDvQFCTM-1689855767485)(C:\Users\11794\AppData\Roaming\Typora\typora-user-images\image-20230719204622485.png)\] 32位序号/32位确认…