HotSpot 虚拟机对象存储逻辑

从IMU数据到精准定位:手把手推导ESKF中的四元数运动学与误差状态方程 本文详细介绍了如何从IMU数据推导ESKF中的四元数运动学与误差状态方程,实现精准定位。通过分析误差状态卡尔曼滤波器的优势、四元数表示的必要性,以及IMU测量模型和运动学基础,提供了完整的数学推导和实现要点。适合机器人定位和自动驾驶领域的算法工程师参考,帮助优化导航系统性能。 阅读详情

对象的创建

Java 是一门面向对象的编程语言,Java 程序运行过程中无时无刻都有对象被创建出来。当 Java 虚拟机遇到一条字节码 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。

在类加载检查通过后,接下来虚拟机将为新生对象分配内存。如果 Java 堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(Free List)。选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有空间压缩整理(Compact)的能力决定。因此,当使用 Serial、ParNew 等带压缩整理过程的收集器时,系统采用的分配算法是指针碰撞,既简单又高效;而当使用 CMS 这种基于清除(Sweep)算法的收集器时,理论上就只能采用较为复杂的空闲列表来分配内存。

对象创建在虚拟机中是非常频繁的行为,即使仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象 A 分配内存,指针还没来得及修改,对象 B 又同时使用了原来的指针来分配内存的情况。解决这个问题有两种可选方案:一种是对分配内存空间的动作进行同步处理——实际上虚拟机是采用 CAS 配上失败重试的方式保证更新操作的原子性;另外一种是把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在 Java 堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local AllocationBuffer,TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。内存分配完成之后,虚拟机必须将分配到的内存空间(但不包括对象头)都初始化为零值,如果使用了 TLAB 的话,这一项工作也可以提前至 TLAB 分配时顺便进行。

Java 虚拟机还要对内存分配后的对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码(实际上对象的哈希码会延后到真正调Object::hashCode()方法时才计算)、对象的 GC 分代年龄等信息。这些信息存放在象的对象头(Object Header)之中。根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了。但是从Java 程序的视角看来,对象创建才刚刚开始——构造函数,即 Class 文件中的 <init>()方法还没有执行,所有的字段都为默认的零值,对象需要的其他资源和状态信息也还没有按照预定的意图构造好。接下来执行init()函数经行初始化,java中的对象才算创建完成。

HotSpot 解释器代码片段

// 确保常量池中存放的是已解释的类
if (!constants -> tag_at(index).is_unresolved_klass()) { // 断言确保是 klassOop 和instanceKlassOop
 oop entry = (klassOop) *constants->obj_at_addr(index);
 assert(entry->is_klass(), "Should be resolved klass");
 klassOop k_entry = (klassOop) entry;
 assert (k_entry -> klass_part()->oop_is_instance(), "Should be instanceKlass");
 instanceKlass * ik = (instanceKlass *) k_entry -> klass_part();
 // 确保对象所属类型已经经过初始化阶段
 if (ik -> is_initialized() && ik -> can_be_fastpath_allocated()) {
 // 取对象长度
 size_t obj_size = ik -> size_helper();
 oop result = NULL;
 // 记录是否需要将对象所有字段置零值
 bool need_zero = !ZeroTLAB; // 是否在 TLAB 中分配对象
 if (UseTLAB) {
 result = (oop) THREAD -> tlab().allocate(obj_size);
 }
 if (result == NULL) {
 need_zero = true; // 直接在 eden 中分配对象 retry:
 HeapWord * compare_to = *Universe::heap () -> top_addr();
 HeapWord * new_top = compare_to + obj_size;
 // cmpxchg 是 x86 中的 CAS 指令,这里是一个 C++方法,通过 CAS 方式分配空间,并发失败的话,转到 retry 中重试直至成功分配为止
 if (new_top <= *Universe::heap () -> end_addr()){
 if (Atomic::cmpxchg_ptr (new_top, Universe::heap() -> top_addr(),
compare_to) !=compare_to)
 goto retry;
 }
 result = (oop) compare_to;
 }
 }
 if (result != NULL) {
 // 如果需要,为对象初始化零值
 if (need_zero) {
 HeapWord * to_zero = (HeapWord *) result + sizeof(oopDesc) / oopSize;
 obj_size -= sizeof(oopDesc) / oopSize;
 if (obj_size > 0) {
 memset(to_zero, 0, obj_size * HeapWordSize);
 }
 }
 // 根据是否启用偏向锁,设置对象头信息
 if (UseBiasedLocking) {
 result -> set_mark(ik -> prototype_header());
 } else {
 result -> set_mark(markOopDesc::prototype ());
 }
 result -> set_klass_gap(0);
 result -> set_klass(k_entry);
 // 将对象引用入栈,继续执行下一条指令
 SET_STACK_OBJECT(result, 0);
 UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
 }
}

对象的内存布局

在 HotSpot 虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
对象头部分包括两类信息。第一类是用于存储对象自身的运行时数据,如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等,这部分数据的长度在 32 位和 64 位的虚拟机中分别为 32 个比特和 64 个比特,官方称它为“Mark Word”。Mark Word 被设计成一个有着动态定义的数据结构,以便在极小的空间内存储尽量多的数,根据对象的状态复用自己的存储空间。

请添加图片描述

对象头的另外一部分是类型指针,即对象指向它的类型元数据的指针,Java 虚拟机通过这个指针来确定该对象是哪个类的实例。

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

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

对象的访问定位

主流的访问方式主要有使用句柄和直接指针两种:

  1. 句柄访问,Java 堆中将可能会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息。

  2. 直接指针,Java 堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference 中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问的开销。

这两种对象访问方式各有优势,使用句柄来访问的最大好处就是 reference 中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference 本身不需要被修改。使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销.
请添加图片描述

博途V17 & PLCSIM & FactoryIO 连接教程 PLCSIM V17 和 FactoryIO 连接教程前言一、软件版本二、连接步骤1. 连接PLCSIM2. 博途编程3.连接FactoryIO总结 前言 该文详细讲解了新手入门博途,并通过使用自带PLCSIM和FactoryIO实现连接。 一、软件版本 TIA Portal V17 S7-PLCSIM V17 Factory IO V2.5.1 二、连接步骤 1. 连接PLCSIM 打开PLCSIM 进入项目模式 新建仿真项目 2. 博途编程 将F 阅读详情

相关推荐

Abaqus 气动软体抓取器:从超弹性材料建模到抓取力仿真全流程解析

本文详细解析了Abaqus在气动软体抓取器仿真中的应用全流程,涵盖超弹性材料建模、接触设置优化到抓取力仿真等关键环节。通过实战案例分享Yeoh模型参数拟合、不可伸展层建模技巧及收敛优化策略,帮助工程师高效解决有限元仿真中的常见问题,提升软体机器人研发效率。

weixin_28689193的博客 238

Java虚拟机:Java内存区域

动机最近一段时间细读了周志明的《深入理解Java虚拟机》,加上近期自己要在组内进行知识分享,于是打算把分享的主题就定为JVM方面的,在制作PPT的过程,反复把此书翻了不少遍,于是想着顺便整理出一份笔记供大家学习。由于自己的水平有限加上为了保证内容的准确性,因此文章大部分内容取自此书的原内容,少部分内容为从知乎R大和其他对JVM有深入理解的人学习而来。文章地址:http://blog.csdn.ne...

程序员囧辉 3249

2018南京大学计算机夏令营机试

1. Count number of binary strings without consecutive 1’s Given a positive integer n(3≤n≤90), count all possible distinct binary strings of length n such that there are no consecutive 1's . Examples...

climber16的博客 1万+

[JVM] java虚拟机内存管理机制及垃圾收集

Table of Contents 虚拟机内存结构详解 程序计数器 虚拟机栈 JVM stack 本地方法栈 native method stack 常量池 堆 heap 方法区 method area 为什么要放弃永久代permnent generation? 局部变量和类变量内存布局 String对象存储? 对象的内存布局?虚拟机栈中的引用如何和堆中的对象产生关联的? ...

topdeveloperr的博客 1592

Docker+vLLM内网离线部署Qwen3 流程

本文介绍了在CentOS 7系统下进行VLLM容器化部署Qwen3-32B大模型的内网离线方案。首先需准备Nvidia显卡驱动、CUDA 12.4和Docker环境。通过联网机器拉取vllm/vllm-openai镜像并导入内网服务器,同时从魔塔社区下载模型文件。部署时使用docker run命令启动容器,配置了GPU资源、内存共享、端口映射等参数,特别指定了模型路径、并行计算、内存利用率等关键参数。该方案实现了大模型在内网环境的安全高效部署,适用于需要离线运行的AI推理场景。

qq_42881308的博客 1043

电路抗干扰原理及应用(1)

电路抗干扰原理——介绍干扰源、干扰途径、抗干扰技术(接地技术)

qq_34758408的博客 2067

Java并发——Synchronized关键字和锁升级,详细分析偏向锁和轻量级锁的升级

Synchronized关键字详解synchronized的基本认识漫谈对象存储(仅针对于hotspot虚拟机)锁的存储synchronized锁的升级偏向锁轻量级锁重量级锁 synchronized的基本认识 数据不安全性的本质在于:共享数据存在并发操作 jdk1.6对synchronized进行了优化,引入了偏向锁和轻量级锁的概念。 synchronized有三种加锁的方式,不同的修饰类型,...

weixin_43954926的博客 560

Nginx健康检查实战:如何用第三方模块提升服务可用性(附详细配置)

本文深入探讨了如何通过集成第三方健康检查模块来提升Nginx的服务可用性。文章对比了Nginx默认被动检查的局限性,并详细介绍了主动健康检查的核心优势、主流模块选型,以及以nginx_upstream_check_module为例的实战部署与配置指南,帮助运维人员构建更可靠的负载均衡架构。

weixin_28331555的博客 308

大厂面试题Object object = new Object()

1.请解释一下对象的创建过程 2.Class对象在堆还是方法区中 3.对象在内存中的存储布局 4.对象头具体包括什么 5.对象怎么定位 6.对象怎么分配 7.Object object = new Object(); 在内存中占用多少字节

qq_44300280的博客 1816

SCADA|KingSCADA通过组合框选择修改变量的值

《SCADA项目中组合框控件的使用方法》 本文详细介绍了SCADA系统中组合框(下拉框)控件的应用方法。通过具体示例演示如何实现:当选择"雷工笔记"时变量值为100,选择"雷工杂谈"时为200,选择"小李食代"时为300的功能。主要实现步骤包括:1)创建项目并添加组合框控件;2)设置Items选项及对应数值;3)创建内存变量;4)使用GetCurrentIndex()和GetItemData()方法;5)在选项变化事件中编写脚本。该控件在SCADA

做有价值的事,积累有价值的笔记! 867

【云原生】阿里云SLS日志自定义字段标签实现日志告警

样例:一个典型的java log初始化日志格式把日志中的INFO提取出来做为一个标签,在开源日志框架中,配置采集器实现- regex:在阿里云SLS日志服务同样可以实现,首先我们进入到一个日志库,查看数据接入,我这里使用的是Logtail工具在【处理配置】中,选择【处理插件组合】,新增提取字段插件原始字段:填写要被提取的日志,对应原始字段的内容做正则结果字段:正则表达式对应的捕获组对应结果:捕获组 匹配内容 说明1 ywgy-app-service 服务名称。

CN_Eden 743

《深入理解java虚拟机》第二章读书心得

刚学习完了这本书的第二章,我做一个总结。 java虚拟机在运行是的数据区如下图所示: 程序计数器:内空间比较小,可以看作是当前线程所执行的字节码的行号指示器。 Java虚拟机栈:虚拟机栈描述的是java方法执行的内存模型 本地方法栈:作用和虚拟机栈类似,只不过本地方法栈是为虚拟使用到的Native方法服务 Java堆:java虚拟机所管理的内存中最大的一块,并且在虚拟机启动时候创...

王魂风气的博客 210

SAP CPI 如何代理第三方接口?

代理接口是指在系统 A 和系统 B 之间引入一个中间层(如 SAP CPI),由中间层负责接收来自系统 A 的请求,进行必要的处理或转换,然后将请求转发给系统 B。由于 OData 服务通常使用 JSON 格式的数据,而某些内部系统可能需要 XML 格式,因此我们添加一个 JSON 到 XML 的转换器,以确保数据格式的兼容性。为了确保返回的数据格式符合内部系统的要求,添加一个 XML 到 JSON 的转换器,将 OData 服务的响应从 XML 转换为 JSON。在集成包中,添加一个新的集成流,命名为。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1749

java内存区域与内存溢出异常_Java内存区域与内存溢出异常

一、概述Java开发者在开发过程中基本不用关心内存的管理,因为虚拟机自动内存管理机制已帮你打理好一切。开发者只需要将重点放在代码实现业务逻辑上,基本可以将内存的管理交给虚拟机去完成。当然,也正是虚拟机的管理,如果我们不了解Java虚拟机如何管理、使用内存,一旦出现内存方面的问题,那排查错误也就成为一项艰难的工作。真的是让人欢喜让人忧啊!二、运行时数据区域(Run-Time Data Areas)J...

weixin_39671631的博客 115

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

JVM(七)内存空间

JVM内存空间 JVM规范在程序运行期间定义了不同的数据区域.有一些区域跟随JVM的创建销毁.而有些区域则是线程独有的,线程独有的区域会跟随线程的创建与销毁. 在不同版本和不同厂商的JVM版本中,都会有较大差异. 本文基于JDK8,HotSpot虚拟机进行的总结 JVM规范内的运行时数据区域 程序计数器(The pc Register) JVM支持多线程,每个线程都有自己的程序计数器.当线程执行中...

r09er的博客 369

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

你能讲讲 JVM 运行时内存空间吗

JVM内存空间 JVM 规范在程序运行期间定义了不同的数据区域,有一些区域跟随 JVM 的创建销毁,而有些区域则是线程独有的,线程独有的区域会跟随线程的创建与销毁。 在不同版本和不同厂商的 JVM 版本中都会有较大差异,本文基于 JDK8 HotSpot 虚拟机进行的总结。 JVM 规范内的运行时数据区域 程序计数器(The pc Register) JVM 支持多线程,每个线程都有自己的程序计数器。当线程执行中的时候,程序计数器会包含线程的执行点(前提是方法不是 native 方法)。 虚拟机栈(Java

weixin_47143210的博客 350

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

JAVA内存管理

JAVA内存区域与内存溢出异常 1.java虚拟机管理的内存各个区域 1.1程序计数器 这是一块占比比较小的内存空间,有学习过《操作系统》这门课程的同学应该很熟悉,它是用来记录下一条要执行的指令的地址,在java虚拟机中,字节码解释器就是通过改变这个计数器的数值来选取下一条需要执行的字节码指令(可是后面又说线程如果是正在执行一个java方法,那么计数器记录的是正在执行的方法,如果执行的是本地方法,这个计数器的值就为空,这里给我留下了一个问题,待查明后修改)。 程序计数器是线程私有的,每一条线程都需要独立

weixin_37619756的博客 201

JVM(七)JVM内存空间

JVM内存空间 在JVM运行期间,会将内存划分为不同的区域 在不同版本和不同厂商的JVM版本中,都会有较大差异. 本文基于JDK8,HotSpot虚拟机进行的总结 虚拟机栈(Stack) 线程独有的内存空间,每个方法在执行时候会形成一个栈帧,用于存在这个方法的局部变量,操作数栈,动态链接,方法返回等信息. 程序计数器(Program counter) 线程独有,描述的是JVM执行过...

r09er的博客 243

逃逸分析--代码三大优化策略(堆不是分配对象存储的唯一选择)

堆是分配对象存储的唯一选择吗?答案是:不是的 随着JIT编译器的发展与逃逸分析逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所以对象都分配到堆上也渐渐变得不是那么绝对了。 在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收。这也是最常见的堆外存储技术。 如何将堆上的对象分配到栈,需要使用逃逸分析手段。Java jdk .

qq_43012792的博客 4153

cs0116 命名空间不能直接包含字段或方法之类的成员_问懵逼:你能讲讲 JVM 运行时内存空间吗?...

点击蓝色小字关注!关注一下你就不会有bug!JVM内存空间JVM 规范在程序运行期间定义了不同的数据区域,有一些区域跟随 JVM 的创建销毁,而有些区域则是线程独有的,线程独有的区域会跟随线程的创建与销毁。在不同版本和不同厂商的 JVM 版本中都会有较大差异,本文基于 JDK8 HotSpot 虚拟机进行的总结。JVM 规范内的运行时数据区域程序计数器(The pc Register)...

weixin_31714129的博客 889

元空间和直接内存_问懵逼:你能讲讲 JVM 运行时内存空间吗?

点击蓝色小字关注!关注一下你就不会有bug!JVM内存空间JVM 规范在程序运行期间定义了不同的数据区域,有一些区域跟随 JVM 的创建销毁,而有些区域则是线程独有的,线程独有的区域会跟随线程的创建与销毁。在不同版本和不同厂商的 JVM 版本中都会有较大差异,本文基于 JDK8 HotSpot 虚拟机进行的总结。JVM 规范内的运行时数据区域程序计数器(The pc Register)...

weixin_39831104的博客 455

JVM的自动内存管理机制

最近开始学习JVM,这里整理一下方便以后复习。 运行时数据区域 程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器通过程序计数器选取下一条要执行的指令。需要注意的是没条线程都有一个独立的程序计数器,互不影响,独立存储。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码的指令地址;对于Native 方法(DLL库之类的...

qq_15983549的博客 310

JVM内存结构 内存模型 内存对象

JVM内存结构、Java内存模型和Java对象模型,是什么 首先,这三个概念是完全不同的三个概念。本文主要目的是对这三个概念加以区分以及做简单的介绍。而这每一个知识点都是又都是比较复杂的。以后会单独写文章做详细介绍。 Jvm内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途。 其中有些...

qq_35129986的博客 302

JDK系列02:Java内存分区详解,栈、堆、方法区、本地方法栈底层原理图解

摘要:本文深入解析Java内存分区,重点讲解JVM运行时数据区的5大核心区域(程序计数器、Java虚拟机栈、本地方法栈、堆、方法区),区分线程私有与共享特性。详细剖析各区域底层原理、存储内容、生命周期及常见异常(OOM、栈溢出),特别针对方法区在JDK6/7/8的版本差异(永久代→元空间)进行对比。文章结合图解与调优参数,帮助开发者深入理解内存管理机制,解决实际开发中的内存问题,并应对相关面试考点。

深耕 Java 后端与企业级软件架构设计,专注微服务、分布式系统、大模型 AI 应用架构落地。 264

java 分代_HotSpot中的“世代/分代”(Generations)概念 for Java 10

Java的优势之一是它对开发者屏蔽了复杂的内存分配和垃圾回收,但当垃圾回收成为瓶颈时,了解它的内部实现机制是有必要的。当运行时没有任何指针指向一个对象时,那这个对象就是个“垃圾”。最直截了当的垃圾收集算法遍历所有“可达”对象,剩下所有的对象都视为“垃圾”????。这种算法的时间消耗跟“存活”的对象数量成正比,对于持有大量“存活”对象的大型程序禁止这么干。采用分代收集的虚拟机整合了不同的垃圾回收算法。不成...

weixin_34891592的博客 309

伙伴算法的核心思想是回收时进行相邻块的合并_Java SE 14 Hotspot 虚拟机垃圾回收调优指南...

# 优化目标与策略(Ergonomics)垃圾收集器、堆和运行时编译器默认选择G1(Garbage First)收集器GC线程的最大值受限于堆大小和可用的CPU资源初始堆空间为物理内存的1/64最大堆空间为物理内存的1/4分层编译器,同时使用C1和C2可以将 Java HotSpot VM 垃圾收集器配置为优先满足两个目标之一:最大暂停时间和应用吞吐量。如果首选目标得到满足,收集器将尝试...

weixin_39737951的博客 174
上一篇: go语言中的数组与切片
下一篇: 垃圾回收算法概述和HotSpot算法细节
过去日记
过去日记 新星创作者: 后端开发技术领域 新星创作者: 后端开发技术领域
博客等级 码龄3年 5343粉丝 225原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过去日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值