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

这篇具有很好参考价值的文章主要介绍了【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

一、对象的生命周期

对象的生命周期主要分为创建、使用、销毁这三大阶段。从它被创建的一刻开始,到它被垃圾收集器(Garbage Collector)回收的一刻结束

  • 对象的创建。包括:1、类的加载 2、内存的分配 3、初始化零值 4、设置对象头 5、执行init方法(具体操作步骤请看上述内容,其中步骤1的类加载还包括类的加载过程及类加载器及双亲委派型型)
  • 对象的使用。对象创建后就可以被使用,可以被其他的对象使用或是被直接使用,如果一个对象没有被其他任何对象引用它,那么它就处于可回收状态。
  • 对象的销毁。当对象不再被引用,或者程序结束时,垃圾收集器会将其标记为可回收。具体的回收时间取决于垃圾收集器的运行策略。一旦对象被回收,它就不再存在

二、对象的创建

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中存储的就是对象的句柄地址,而句柄中包含了对象实例数据对象类型数据各自的具体地址信息。
    【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问,java,学习,jvm
    优点:
    使用句柄方式访问对象最大的好处是reference中存储的是稳定的句柄地址,在对象被移动时(垃圾回收会造成堆中对象地址变动),只需要同步变动句柄池中对象实例数据指针即可。
    缺点
    访问对象需要多访问一次句柄池,多了一次指针定位的开销。

  • (二)直接指针
    使用直接指针方式访问,reference中存储的就是堆中的对象地址,相比与使用句柄访问节省了一次指针定位的时间开销。而我们的Java应用中,访问对象是非常频繁的,所以都使用直接指针的访问方式一步到位。HotSpot虚拟机主要使用的就是这种方式来进行对象访问。
    【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问,java,学习,jvm文章来源地址https://www.toymoban.com/news/detail-599992.html

到了这里,关于【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Java Platform, Standard Edition HotSpot 虚拟机垃圾收集调优指南

    Java Platform, Standard Edition HotSpot 虚拟机垃圾收集调优指南

    Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide (JDK8官方) 参考文档 目录 前言 观众 文档辅助功能 相关文件 惯例 1 简介 2 工效学 垃圾收集器、堆和运行时编译器默认选择 基于行为的调优 最长停顿时间目标 吞吐量目标 Footprint占用空间目标 调整策略 3 代

    2024年02月04日
    浏览(10)
  • 虚拟机下Linux系统磁盘扩容

    虚拟机下Linux系统磁盘扩容

    在VM虚拟机中,我们经常会选择默认磁盘大小20G,用着用着才发现20G不够用,服务启动不了,就很尴尬,让我们今天一起来学习下,如何在虚拟机给磁盘扩容。 背景:我们使用 df -h 命令,发现磁盘使用率已经37%,我们给此磁盘扩容,简单演示下扩容的过程。 1:虚拟机关机,

    2024年02月06日
    浏览(6)
  • CentOs虚拟机下openGauss的配置使用

    虚拟机平台 VMware 服务器端 CentOS 7.9 本机系统 Windows 10 部署版本 OpenGauss 1.1.0 虚拟机 VMware 本机已配置,该部分省略 CentOS 7.9 安装 下载镜像源 CentOS-7-x86_64-DVD-2009.iso 虚拟机中选中镜像进行安装 设置 内存设置为 2GB 处理器设置为 2 网络默认即可 声卡和打印机不使用直接进行了移

    2024年03月10日
    浏览(7)
  • 【杂】虚拟机下ubuntu无法访问github

    【杂】虚拟机下ubuntu无法访问github

    虚拟机下ubuntu访问github有时无法访问,本人尝试多个解决方法,最终通过修改host解决。 解决方法一:修改host(见步骤1-4)。 其他可以尝试的修改方法:关闭防火墙试试/切换节点 将github.com的ip地址添加到hosts文件末尾,如下图所示。 将github.global.ssl.fastly.net的ip地址添加到

    2024年01月20日
    浏览(14)
  • 9.Linux虚拟机下Hive的安装配置

    9.Linux虚拟机下Hive的安装配置

    Hadoop及相关组件搭建指导WeChat:h19396218469 hadoop-3.1.3 jdk-8u162-linux-x64 apache-hive-3.1.2-bin 本案例软件包:链接:https://pan.baidu.com/s/1ighxbTNAWqobGpsX0qkD8w 提取码:lkjh(若链接失效在下面评论,我会及时更新) 1.解压Hive安装包 先进入文件存放目录Downloads,再执行如下命令。 上面的dblab

    2024年02月08日
    浏览(8)
  • [正式学习java①]——java项目结构,定义类和创建对象,一个标准javabean的书写

    [正式学习java①]——java项目结构,定义类和创建对象,一个标准javabean的书写

    目录 一、创建第一个java文件 二、 初始类和对象 三、符合javabean规范的类 要想写代码,你得有文件啊 以前的创建方式: 右键新建文本文档,开始写代码,写完改后缀名,保存……这样文件一旦多了很难管理 现在我们要规范项目结构 每一个类就是一个java文件,每一个包存放

    2024年02月08日
    浏览(8)
  • Ubuntu虚拟机下docker容器的自启动设置

    一.背景 希望启动虚拟机后,当然docker自动启动了的情况下,我们希望对应的容器也自动启动,比如tomcat或者redis。 二.设置过程 1.如果是已经有容器实例了,修改吧 命令为:sudo docker update --restart=always  容器id或者名称 不知道名称或者id,可以输入命令查看:sudo docker ps -a 关

    2024年02月09日
    浏览(4)
  • 【在Linux虚拟机下如何添加磁盘并分区挂载】

    【在Linux虚拟机下如何添加磁盘并分区挂载】

    Linux文件系统是指Linux操作系统中用于组织、存储和管理文件和目录的一套机制。Linux文件系统采用树形结构,以根目录(/)作为根节点,所有文件和目录都位于根目录下或其子目录中。类似树结构。 Linux支持多种不同类型的文件系统,包括ext2、ext3、ext4、XFS、JFS、Btrfs等。其

    2024年02月06日
    浏览(12)
  • VMware虚拟机下Ubuntu中geth和truffle更换版本解决

    VMware虚拟机下Ubuntu中geth和truffle更换版本解决

    虚拟机:Vm16Pro 安装系统:Ubuntu20.04.6 Ubuntu官网地址:https://ubuntu.com/download/ 虚拟机Ubuntuan安装教程🔗: VMware虚拟机安装Ubuntu20.04详细图文教程_vmware ubuntu-CSDN博客  1.在Ubuntu终端输入一下命令: 2.查看geth版本 执行上述命令后发现Geth的版本是1.13.5-stable,1.13.5-stable的版本在后续操

    2024年02月03日
    浏览(9)
  • 解决ubuntu新虚拟机下apt update报错和git clone失败的问题

    新的虚拟机,安装git发现apt不能正常使用,报错如下 第一反应是考虑到apt源配置可能有问题,用vi /etc/apt/source.list打开源发现没有问题,用ping测试网络,ping百度报错unkown host,ping 8.8.8.8可以通,应该是域名解析错误,vi /etc/resolv.conf更改nameserver,输入两个8.8.8.8和8.8.4.4,然后

    2024年02月03日
    浏览(17)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包