JVM HotSpot 虚拟机: 对象的创建, 内存布局和访问定位

news2024/12/28 6:05:46

目录

前言

对象的创建

对象的内存布局

对象的访问定位  


前言

        了解JVM的内存区域划分之后, 也大致了解了java程序的内存分布模型, 也了解它里面的内存区域里面的类型和各个类型的作用, 接下来我们进一步从对象创建到访问的角度, 来看看这些内存区域之间是怎么关联起来的. 


对象的创建

         对象的创建在java语言中最直接的体现就是new这个关键字, 虚拟机在遇到一个new指令(这个指令是JVM内部指令集的一部分, 直到JVM如何对对象分配内存并初始化 )的时候, 首先会去检查这个指令的参数(例如类名, 你要实例化的是哪一个类的)是否能在常量池中定位到一个符号引用.

        这些类的信息一般都存储在常量池中, 常量池是方法区的一部分, 用于存储各种字面量和符号引用, 常量池在编译的时候就已经确定了. 在运行时,JVM会将这些符号引用解析为直接引用,即指向内存中实际的类或字段等的指针或偏移量. 

什么符号引用, 什么是直接引用? 

符号引用用于表示一个类、接口、字段或方法的引用

在java语言层面,  一个类被编译之后就会生成字节码文件, 也就是Class文件, class文件中有着类文件的常量池, 在这个类文件被加载的时候, 对应的常量池也会被加载到虚拟机运行时常量池中去. 

        简而言之就是JVM会去检查new指令对应的类的Class文件是否被加载, 如果被加载, 那么就一定可以在运行时常量池中找到对应的类符号引用, 如果找不到就进行类的加载过程. 

        例如当执行到new MyClass()时,JVM会检查MyClass是否已经被加载到内存中。如果没有,JVM会触发类加载过程,将MyClass的字节码加载到内存中,并创建一个java.lang.Class对象来表示这个类。在这个过程中,MyClass的符号引用(如类的全限定名)会被解析为直接引用(如指向内存中Class对象的引用)

        在类加载校验通过之后, 就是为对象分配内存的时候, 创建对象所需的内存其实是在类加载之后就可以完全确定的.

        接下来的问题就是如何分配内存, 有两种方式: 

  • 指针碰撞
  • 内存块列表

        指针碰撞其实就是假设java内存是绝对规整的, 例如将一个内存划分为两块, 一块是已经被使用的内存区域, 一块是空闲的内存区域 他们之间有一个指针来标记当前已被使用的内存的地址, 如下: 

        这个指针也被称为分界点指针

        另外一种就是内存块列表: 

        java虚拟机的堆内存, 一般不是规整的, 那么空闲的内存和已使用内存就会交错在一起, 这个时候使用分界点指针就没什么意义, 此时虚拟机就必须维护一个表, 这个表将java的堆内存划分为很多个内存块, 表里面的指针分别指向对应的内存块, 并且记录了那些内存是可用的, 分配的时候, 从内存块中找出一个拥有足够大的内存空间的给实例对象即可.

        具体采用那种方式, 需要看虚拟机的具体实现.  比如有的虚拟机是内存规整的, 就可以使用指针碰撞的方法. 而是否规整又取决于内存的回收机制, 如果一个垃圾回收器可以将带有内存碎片的空间给整理成上述的规整形态, 就可以使用指针碰撞的形式. 

        上述只是对象的内存分配, 还有一个问题就是在分配完成内存之后, 修改指针引用的时候会出现并发问题, 例如, 给引用A创建好一个对象之后, 需要将对象的地址付给A的指针, 但是在还没赋值好的时候, 另外一个对象使用了这个A的指针, 就会出现问题. 

        针对这个问题, JVM采用了CAS + 失败重试的方法保证 分配和更新引用 这两个操作的原子性, 另外一种做法就是使用线程分配缓冲, 一个线程只能访问自己本地缓存, 只有本地缓冲区用完了, 才可以使用锁区同步申请缓存空间.

        内存分配完之后, 为分配好的内存的对象进行初始化零值操作, 使程序能访问到这些字段的数据类型所对应的零值

        接下来就是对象头的设置. 例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到真正调用Object::hashCode()方法时才计算)、对象的GC分代年龄等信息

        这些东西都完成之后, 剩下的就是根据程序员自己的代码逻辑进行初始化操作, 也就是调用构造方法, 即Class文件中的<init>()方法. 这个方法只会执行一次, 按照程序员的意愿对对象进行初始化,这样一个真正可用的对象才算完全被构造出来

对象的内存布局

         在HotSpot中, 对象在内存中的存储布局可以划分为3个: 

  • 对象头
  • 实例数据
  • 对齐填充

        对象的对象头部分包括两类信息, 第一类是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32个比特和64个比特,官方称它为“Mark Word”. 

        对象头的另外一部分是类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例, 如果对象是一个Java数组,那在对象头中还必须有一块用于
记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是如果数组的长度是不确定的,将无法通过元数据中的信息推断出数组的大小.

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

        第三部分就是内存对齐: 这并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是任何对象的大小都必须是8字节的整数倍。对象头部分已经被精心设计成正好是8字节的倍数(1倍或者2倍),因此,如果对象实例数据部分没有对齐的话,就需要通过对齐填充来补


对象的访问定位  

        接下来你应该需要了解 一个对象存储在堆区该怎么访问? 我们之前提到过, 一个对象的实例创建并调用init初始化之后, 就是一个完全体的对象了, 然后这个对象会被赋值给java虚拟机栈中的一个引用变量, 然后你可以通过这个变量去访问他

        这个引用变量也被称为reference数据, 通过它来操作堆中的数据, 但是由于它只是规定了它指向一个内存对象, 但是没有明确的指明该如何指定, 所以说不同的场景下的定位方法, 有着不同的性能区别. 

        一般有下面这两种: 

  • 句柄
  • 直接引用

        如下图: 

        句柄引用其实就是利用了java内存布局中对象头和实例数据的存储可以在不同的运行时区域, 例如, java的对象头可以存储在方法区, 实例数据则存放在堆区. 然后在堆区中生成一个句柄池子, 池子中有很多句柄对象, 一个句柄对象可以被一个reference类型数据给引用, 然后一个句柄对象就是表示一个完整的对象信息, 里面包括对实例数据和对象头数据的引用. 

句柄引用
句柄引用

        下面这一种就是最直接的做法, reference指向的就是java内存中对象的地址. 然后对象的类型数据则是存放的指针, 真正的类型数据存放在方法区中.  

直接引用

区别? 

  • 使用句柄池, 就需要单独在内存中开辟句柄池空间 , reference存储的使句柄池, 访问句柄池中的句柄对象之后, 还需要通过句柄对象中的指针, 再寻找一次真正的实例对象数据和对象类型数据. 相当于多了一次寻找的开销, 最大的优势就是句柄池是稳定的, 对象无论怎么修改, 只要保持和句柄池中的数据指向一致即可,  而对于reference来说, 对象内存地址的修改是透明的(例如GC的情况下 ,  对象的地址可能就会发生变化.), reference不需要改变句柄对象的指向.
  • 使用指针访问就直接很多, reference指向的既是对象, 可以直接访问其实例数据.  免去了一次寻找对象实例数据的开销, 由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本

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

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

相关文章

【C++篇】C++类与对象深度解析(二):类的默认成员函数详解

文章目录 【C篇】C类与对象深度解析&#xff08;二&#xff09;前言1. 类的默认成员函数2. 构造函数2.1 函数名与类名相同2.2 无返回值2.3 对象实例化时系统会自动调用2.4 构造函数可以重载2.5 默认构造函数的生成规则2.6 无参构造函数与全缺省构造函数的关系2.7 内置类型与自定…

五、(JS)window中的定时器

一、为什么叫做window中的定时器 我们在全局中会用到一些函数&#xff0c;比如说alert函数&#xff0c;prompt函数&#xff0c;setTimeout等等 我们有在这里定义过这些函数吗&#xff1f;很明显没有。可见我们这些函数都是来自于window。 所以还可以写成window.setTimeout。…

Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解

目录 一、Linux软件包管理器 - yum&#xff08;ubuntu用apt代替yum&#xff09;1、Linux下安装软件的方式2、认识 yum3、查找软件包4、安装软件5、如何实现本地机器和云服务器之间的文件互传 二、Linux编辑器 - vim1、vim 的基本概念2、vim 下各模式的切换3、vim 命令模式各命令…

【Linux篇】TCP/IP协议(笔记)

目录 一、TCP/IP协议族体系结构 1. 数据链路层 &#xff08;1&#xff09;介绍 &#xff08;2&#xff09;常用协议 ① ARP协议&#xff08;Address Resolve Protocol&#xff0c;地址解析协议&#xff09; ② RARP协议&#xff08;Reverse Address Resolve Protocol&…

[Meachines] [Easy] Sauna DC域+AS-REP+TGT票证窃取+AutoLogon凭据+DCSync攻击

信息收集 IP AddressOpening Ports10.10.10.175TCP:53,80,88,135,139,389,445,464,593,3268,3269,5985 $ nmap -p- 10.10.10.175 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 53/tcp open domain? | fingerprint-strings: | DNSVersionBindReqTCP…

电容的不同材质对应的温度范围

电容的温度范围是指电容在不同温度下能够正常工作的范围。不同材质的电容具有不同的温度特性&#xff0c;以下是一些常见电容材质的温度范围。 C0G/NP0&#xff1a;这类电容具有非常稳定的电气性能&#xff0c;工作温度范围通常为-55℃至125℃。 X7R&#xff1a;X7R材质的电容…

2021高教社杯全国大学生数学建模竞赛C题 Python代码演示

目录 问题一1.1 根据附件 1&#xff0c;对 402 家供应商的供货特征进行量化分析计算供货特征数据标准化对正向指标归一化对负向指标归一化 1.2 建立反映保障企业生产重要性的数学模型熵权法熵权法-TOPSISAHP 1.3 在此基础上确定 50 家最重要的供应商&#xff0c;并在论文中列表…

软件工程毕业设计开题汇总

文章目录 &#x1f6a9; 1 前言1.1 选题注意事项1.1.1 难度怎么把控&#xff1f;1.1.2 题目名称怎么取&#xff1f; 1.2 开题选题推荐1.2.1 起因1.2.2 核心- 如何避坑(重中之重)1.2.3 怎么办呢&#xff1f; &#x1f6a9;2 选题概览&#x1f6a9; 3 项目概览题目1 : 大数据电商…

几个常见的非初等函数

在多个激励共同作用下,其响应恒等于每个激励单独引起的响应之和。这种现象称为线性现象。线性性质使得对这类现象的数学描述大为简化,它是线性系统理论的基础。 本篇博客将简单介绍以下几个常用的非初等函数。 rect 矩形函数(Rectangular Function)sinc 函数(Sinc Function)三…

“树”据结构:并查集从入门到AC

“树”据结构&#xff1a;并查集 前言算法设计代码示例优化相关文章 前言 在一组数据中&#xff0c;数据被分为了不同的集合&#xff0c;那么其中的集合往往可以用树形来表示。而区分集合&#xff0c;与查找集合的元素&#xff0c;就会成为核心的问题。并查集主要就是解决这类…

模特妙善:一位多才多艺的短视频达人,绽放新光彩

模特妙善&#xff0c;在当今多元化的网络时代&#xff0c;短视频已成为人们生活中不可或缺的一部分。而在这一领域中&#xff0c;有一位以其独特魅力和多才多艺而备受瞩目的达人&#xff0c;她就是妙善&#xff0c;本名高艳芳。 模特妙善&#xff0c;出生于山西省的著名景点——…

一款免费的AI搜索工具,提升您的搜索效率!

开搜AI是一款面向大众的、直达答案的AI搜索引擎&#xff0c;它能够为用户问题提供直接、精准的答案&#xff0c;并自动总结重点、生成大纲、思维导图并下载。 开搜AI功能特点 精准结果呈现&#xff1a;开搜AI能够直接呈现精准结果&#xff0c;省去用户翻阅多个的繁琐过程。信…

MyBatis 增删改查【后端 17】

MyBatis 增删改查 引言 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射&#xff0c;将接口和 Java 的 POJOs (…

Adding Placement Constraints

步骤2&#xff1a;添加放置约束 探索一些设计层次结构&#xff0c;并开始放置逻辑元素以创建物理 约束。 1.从Flow Navigator中&#xff0c;选择Open Synthetic Design。综合设计可能 如果您直接进入此步骤&#xff0c;则已经打开。 合成网表打开&#xff0c;显示设备窗口。 2.…

Note091203_Outlook邮件撤回操作

Note091203_Outlook邮件撤回操作 如图所示&#xff1a; step1: 打开outlook step2&#xff1a; 点击已发送邮件&#xff0c;选中目标撤回邮件双击打开&#xff1b; step3&#xff1a; 点击图中2框所示&#xff0c;可看见撤回操作&#xff1b; 以上

Linux常用命令以及操作技巧

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明帮助命令常见的七个linux操作终端实用的技巧跟文件目录…

Leetcode—740. 删除并获得点数【中等】(unordered_map+set+sort)

2024每日刷题&#xff08;162&#xff09; Leetcode—740. 删除并获得点数 算法思想 实现代码 class Solution { public:int deleteAndEarn(vector<int>& nums) {unordered_map<int, int> freq;set<int> st;sort(nums.begin(), nums.end());int n num…

c++234继承

#include<iostream> using namespace std;//public 修饰的成员便俩个和方法都能使用 //protected&#xff1a;类的内部 在继承的子类中可使用 class Parents { public:int a;//名字 protected:int b;//密码 private:int c;//情人public:void printT(){cout << &quo…

关于wp网站出现的问题

问题1 问题1&#xff1a;如果出现这个界面的问题 说明是根目录的index.php编码出了问题&#xff0c;用备份的源文件退换一下即可。 问题2 问题2&#xff1a;如果出现页面错位现象&#xff0c;可能是某个WP插件引起的问题&#xff0c;这里需要逐步排查插件&#xff0c;或者你刚…

论文内容分类与检测系统源码分享

论文内容分类与检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comput…