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

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

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

    2024年02月06日
    浏览(38)
  • 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日
    浏览(39)
  • 【杂】虚拟机下ubuntu无法访问github

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

    2024年01月20日
    浏览(39)
  • 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日
    浏览(46)
  • [正式学习java①]——java项目结构,定义类和创建对象,一个标准javabean的书写

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

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

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

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

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

    2024年02月09日
    浏览(43)
  • 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日
    浏览(33)
  • 解决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日
    浏览(37)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包