JAVA对象头的指针压缩

news2024/11/24 11:45:03

JAVA对象头的指针压缩

文章目录

  • JAVA对象头的指针压缩
    • 对象在JVM中的内存布局
    • 对象的访问定位
    • 压缩实验
      • 实验步骤
        • 压缩策略组合
        • 压缩内容
        • 压缩后的影响
        • 指针压缩的实现
    • JVM内存关键大小

对象在JVM中的内存布局

在 Hotspot 虚拟机中,对象的内存布局主要由 3 部分组成:

  1. 对象头(Header):包含了对象运行时数据Mark Word,Klass Pointer、数组长度(数组对象才会有)。

    • Mark Word:包含对象运行时的信息,包括哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
    • 类型指针(Klass Pointer):即对象指向它的类型元数据的指针。通过直接指针访问对象才需要在对象上存储类型指针,通过句柄访问对象时不需要此指针。Hotspot采用的是直接指针访问对象的方式,所以这类虚拟机中的对象上存储了类型指针。
    • 数组长度:数组对象才会有。
      在这里插入图片描述
      在这里插入图片描述
  2. 实例数据(Instance Data):对象存储的真正有效数据,即当前类型的字段和父类继承的字段

  3. 对应填充(Padding):不一定存在,主要用于占位符。虚拟机中任何对象的大小都是8字节的整数倍,如果对象所占空间不是8字节的整数倍,会进行补充对齐。

对象的访问定位

在对象创建之后,JAVA程序会通过JAVA栈上的reference数据来操作堆上的具体对象。对象的访问方式主要由虚拟机自主实现,主流的有两种方式:

  • 通过句柄访问对象

    JAVA堆中会有一块内存作为句柄池,reference中存储的是对象的句柄地址,在句柄中包含了对象的实例数据和对象的类型数据的地址。

    优势:reference中存储的是稳定的句柄地址,在对象发生移动的时候(比如在垃圾回收的时候会移动对象),只改变了句柄中对象实例数据的地址,而reference不需要修改。

    // 访问图解

  • 通过直接直接访问对象

    JAVA堆中的对象实例数据中存储着对象类型数据的地址,reference中存储的是对象的地址。

    优势:访问速度快,在访问对象的时候比句柄方式少一次指针定位的时间开销,由于对象访问比较频繁,那这个节约的开销积累下来就很可观了。

    // 访问图解

压缩实验

实验步骤

  • 使用的是64位OS

  • 引入查看对象头布局的工具

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>
package org.donny;

import org.openjdk.jol.info.ClassLayout;

/**
 * @author 1792998761@qq.com
 * @description 借助openjdk的jol工具, 查看对象内存的占用情况
 * oop(ordinary object pointer)--对象指针
 * -XX:+UseCompressedOops 默认开启
 * -XX:+UseCompressedClassPointers  默认开启对象头里面的类型指针压缩
 * @date 2023/6/5
 */
public class TestObjectHeader {
    public static void main(String[] args) {

        System.out.println("============Object对象===========");
        ClassLayout layout = ClassLayout.parseInstance(new Object());
        System.out.println(layout.toPrintable());

        System.out.println("============int类型数组对象===========");
        ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
        System.out.println(layout1.toPrintable());

        System.out.println("============复合类型对象===========");
        ClassLayout layout2 = ClassLayout.parseInstance(new CompositeObjectsTest());
        System.out.println(layout2.toPrintable());
    }

    public static class CompositeObjectsTest {
        //8B mark word
        //4B Klass Pointer   如果关闭压缩-XX:-UseCompressedClassPointers或-XX:-UseCompressedOops,则占用8B
        int id;        //4B
        String name;   //4B  如果关闭压缩-XX:-UseCompressedOops,则占用8B
        byte b;        //1B
        Object o;      //4B  如果关闭压缩-XX:-UseCompressedOops,则占用8B
    }
}

在这里插入图片描述

压缩策略组合

VM的配置项

  • UseCompressedOops:压缩当前对象实例数据中的 Klass Pointer 指针
  • UseCompressedClassPointers:压缩当前对象的对象头中 Klass Pointer 指针

对象指针压缩+对象头的类型指针压缩

JDK1.8之后默认开启这两个压缩

VM options: -XX:+UseCompressedOops -XX:+UseCompressedClassPointers

============Object对象===========
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf80001e5
 12   4        (object alignment gap)    
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

============int类型对象===========
[I object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4        (object header: class)    0xf800016d
 12   4        (array length)            0
 16   0    int [I.<elements>             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

============复合类型对象===========
org.donny.TestObjectHeader$CompositeObjectsTest object internals:
OFF  SZ               TYPE DESCRIPTION                 VALUE
  0   8                    (object header: mark)       0x0000000000000005 (biasable; age: 0)
  8   4                    (object header: class)      0xf800f161
 12   4                int CompositeObjectsTest.id     0
 16   1               byte CompositeObjectsTest.b      0
 17   3                    (alignment/padding gap)     
 20   4   java.lang.String CompositeObjectsTest.name   null
 24   4   java.lang.Object CompositeObjectsTest.o      null
 28   4                    (object alignment gap)      
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

对象指针不压缩+对象头的类型指针压缩

VM options:-XX:-UseCompressedOops -XX:+UseCompressedClassPointers

============Object对象===========
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x0000012a6a481c00
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

============int类型对象===========
[I object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x0000012a6a480b68
 16   4        (array length)            0
 20   4        (alignment/padding gap)   
 24   0    int [I.<elements>             N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

============复合类型对象===========
org.donny.TestObjectHeader$CompositeObjectsTest object internals:
OFF  SZ               TYPE DESCRIPTION                 VALUE
  0   8                    (object header: mark)       0x0000000000000005 (biasable; age: 0)
  8   8                    (object header: class)      0x0000012a6aae3210
 16   4                int CompositeObjectsTest.id     0
 20   1               byte CompositeObjectsTest.b      0
 21   3                    (alignment/padding gap)     
 24   8   java.lang.String CompositeObjectsTest.name   null
 32   8   java.lang.Object CompositeObjectsTest.o      null
Instance size: 40 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total

对象指针压缩+对象头的类型指针不压缩

VM options: -XX:+UseCompressedOops -XX:-UseCompressedClassPointers

============Object对象===========
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x000002d380801c00
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

============int类型对象===========
[I object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x000002d380800b68
 16   4        (array length)            0
 20   4        (alignment/padding gap)   
 24   0    int [I.<elements>             N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

============复合类型对象===========
org.donny.TestObjectHeader$CompositeObjectsTest object internals:
OFF  SZ               TYPE DESCRIPTION                 VALUE
  0   8                    (object header: mark)       0x0000000000000005 (biasable; age: 0)
  8   8                    (object header: class)      0x000002d380e63210
 16   4                int CompositeObjectsTest.id     0
 20   1               byte CompositeObjectsTest.b      0
 21   3                    (alignment/padding gap)     
 24   4   java.lang.String CompositeObjectsTest.name   null
 28   4   java.lang.Object CompositeObjectsTest.o      null
Instance size: 32 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total

对象指针不压缩+对象头的类型指针不压缩

VM options: -XX:-UseCompressedOops -XX:-UseCompressedClassPointers

============Object对象===========
java.lang.Object object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x00000234d96d1c00
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

============int类型对象===========
[I object internals:
OFF  SZ   TYPE DESCRIPTION               VALUE
  0   8        (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8        (object header: class)    0x00000234d96d0b68
 16   4        (array length)            0
 20   4        (alignment/padding gap)   
 24   0    int [I.<elements>             N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

============复合类型对象===========
org.donny.TestObjectHeader$CompositeObjectsTest object internals:
OFF  SZ               TYPE DESCRIPTION                 VALUE
  0   8                    (object header: mark)       0x0000000000000001 (non-biasable; age: 0)
  8   8                    (object header: class)      0x00000234d9d33210
 16   4                int CompositeObjectsTest.id     0
 20   1               byte CompositeObjectsTest.b      0
 21   3                    (alignment/padding gap)     
 24   8   java.lang.String CompositeObjectsTest.name   null
 32   8   java.lang.Object CompositeObjectsTest.o      null
Instance size: 40 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total

结果集解释

OFF代表offset
SZ代表size
VALUE代表的十六进制的表示值
alignment/padding gap代表不足8字节的整数倍,填充补齐项
类型开启指针压缩不开启指针压缩
Object16字节16字节
int数组16字节24字节
复合对象32字节40字节

压缩内容

可以发现通过指针压缩可以对以下数据进行压缩:

  1. 对象头信息:64位平台下,原生对象头大小为16字节,压缩后为12字节
  2. 对象的引用类型:64位平台下,引用类型本身大小为8字节,压缩后为4字节
  3. 对象数组类型:64位平台下,数组类型本身大小为24字节,压缩后16字节

主要是对象头里面的kclass指针,即指向方法区的类信息的指针,由8字节变为4字节。 还有就是引用类型指针也由8字节变为4字节

压缩后的影响

压缩后的好处:

  1. 减缓GC的压力: 即每个对象的大小都变小了,就不需要那么频繁的GC了。
  2. 增加CPU缓存的对象指针数量,同时降低CPU缓存的命中率。即CPU缓存本身的大小就小的多,如果采用八字节,CPU能缓存的oop(普通对象指针)肯定比四字节少。

指针压缩的实现

地址总线的根数决定了最大可用内存空间容量。每一个字节(B)的内存空间被视为一个地址单元,整个内存可以看作很多个地址单元组成的数组,每个地址单元有唯一确定的地址来标定,来区分不同的地址单元。地址总线的数量会变化,数据不一定准确可以查询服务器硬件参数确认。

地址总线数目内存上限
32位操作系统32 2 32 2^{32} 232=4GB
64位操作系统36或者46 2 36 2^{36} 236=64GB或者 2 46 2^{46} 246=64TB

一种理解:

当开启指针压缩后,KlassPointer的寻址极限是4 byte × 8 bit=32 bit,即KlassPointer可以存放2^32(=4G)个内存单元地址。

因为每个对象的长度一定是8的整数倍,所以KlassPointer每一数位存放的一定是8的整数倍的地址,即0/8/16/24/32/40/48/64……,也就是4G × 8 = 32G。当分配给JVM的内存空间大于32G时,KlassPointer将无法寻找大于32G的内存地址,因此设置的压缩指针将失效。

第二种理解:

JVM的实现方式是

不再保存所有引用,而是每隔8个字节保存一个引用。例如,原来保存每个引用0、1、2…,现在只保存0、8、16…。因此,指针压缩后,并不是所有引用都保存在堆中,而是以8个字节为间隔保存引用。

在实现上,堆中的引用其实还是按照0x0、0x1、0x2…进行存储。只不过当引用被存入64位的寄存器时,JVM将其左移3位(相当于末尾添加3个0),例如0x0、0x1、0x2…分别被转换为0x0、0x8、0x10。而当从寄存器读出时,JVM又可以右移3位,丢弃末尾的0。(oop在堆中是32位,在寄存器中是35位,2的35次方=32G。也就是说,使用32位,来达到35位oop所能引用的堆内存空间

JVM内存关键大小

  • 当堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址
  • 当堆内存大于32G时,压缩指针会失效,会强制使用64位(即8字节)来对java对象寻址, 那这样的话内存占用较大,会增加GC压力等等

参考文章
https://www.cnblogs.com/xiaomaomao/p/17350075.html
https://blog.51cto.com/u_87851/6326662
https://artisan.blog.csdn.net/article/details/106958768
https://blog.csdn.net/lioncatch/article/details/105919666

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

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

相关文章

OpenMMLab AI 实战营笔记4——MMPreTrain算法库:构建高效、灵活、可扩展的深度学习模型

文章目录 摘要一、工具箱介绍二、丰富的模型三、推理API四、环境搭建——OpenMMLab软件栈五、OpenMMLab重要概念——配置文件六、代码框架七、配置及运作方式经典主干网络残差网络Vison Transformer(VIT)注意力机制自监督学习常见类型SimCLRMAE自监督学习 多模态CLIPBLIPOthers…

jvm 命令和工具

目录 堆内存分析工具 MAT ZProfiler - 线上的mat EagleEye-MProf - 命令行 命令行 线程池排查 jstack jmap -dump jmap -heap jstat 堆内存分析工具 MAT eclipse官方推出的本地内存分析工具&#xff0c;运行需要大量内存&#xff0c;从使用角度来讲&#xff0c;并不…

Mysql查询优化

Mysql查询优化器 在多种情况下,可能会导致查询结果从缓存中清除,例如:. 数据可能已被修改 您可能运行了一条语句,其文本与缓存的语句略有不同(小写/大写,换行符,...) 缓存可能已达到其大小限制之一(内存,查询计数,块等),并决定逐出您的特定查询 高速缓存碎片过多…

DatenLord前沿技术分享 No.26

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;通过软硬件深度融合的方式打通云云壁垒&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。达坦科…

SpringBoot+MyBatisplus搭建校园新闻平台——已开源

概述 开发背景 校园新闻平台是以新闻宣传机构的在线信息发布需求为基础&#xff0c;随着数字化和信息化的快速发展&#xff0c;校园新闻在校园内的传播和沟通中变得越来越重要。学校需要一个有效的管理系统来整合、发布和传播校园新闻&#xff0c;以满足师生、校友和其他利益…

我对测试行业发展和自我价值诉求的思考

测试圈子生态的思考 其实测试的生态&#xff0c;说起来蛮简单的&#xff0c;一个词语概括就是两极分化。有个梗&#xff1a;hand hands&#xff0c;load loads&#xff0c;太贴切了。 两极分化这个词&#xff0c;可以从下面三个维度来看&#xff1a; 薪资 我认识的测试也算不少…

搜索插入位置 力扣 Python

题目描述&#xff1a; 解题代码&#xff1a; class Solution:def searchInsert(self, nums: List[int], target: int) -> int:if target in nums:return nums.index(target)else:nums.append(target)nums.sort()return nums.index(target)题目分析&#xff1a; 时间复杂度…

0804空间直线及其方程-向量代数与空间解析几何

文章目录 1 空间直线方程1.1 空间直线的一般方程1.2 空间直线的对称式方程1.3 空间直线的参数方程1.4 空间直线的两点式方程 3 两直线的夹角4 直线与平面的夹角4.1 定义4.2 夹角的正弦公式 5 例题6 平面束方程结语 1 空间直线方程 1.1 空间直线的一般方程 空间直线L可以看做是…

【3DsMAX】从零开始建房(4)

目录 1. 制作二层主体 2. 制作二楼顶层栏杆 1. 制作二层主体 将二层的长方体转换为可编辑多边形&#xff0c;将左半部分的点向左移动一点距离 选中左右边线进行连接 移动连接线 选中如下的面挤出 选中一条边线&#xff0c;再点击环形 再点击连接 挤出 选中如下一条边线向上移…

【Windows安装】Windows详细安装nginx部署教程

1、先下载直接去官网nginx.org 点击后就会下载&#xff0c;下载完成后开始安装&#xff0c;其实官网已经告诉了如何安装&#xff0c;右侧“documentation -> nginx windows”就有详细的说明&#xff0c;只是英文而已 2、下载完成后&#xff0c;解压缩&#xff0c;运行cmd&a…

【网络工程师必备知识点】从头认识IPv6!

1. IPv6 的背景 IPv4 地址空间已经消耗殆尽&#xff0c;近乎无限的地址空间是 IPv6 的最大优势 2. IPv6 基本报头 在 IPv4 的基础上增加了流标签&#xff0c;去掉了一些冗余字段&#xff0c;使报文头部的处理更 为简单、高效 3. IPv6 扩展报头 是跟在 IPv6 基本报头后面的可…

自定义类型作为map或者unordered_map的key需要额外做哪些事情

文章目录 1、自定义类型作为map的key2、自定义类型作为unordered_map的key 1、自定义类型作为map的key map中有4个参数&#xff0c;前两个参数是key和val的类型&#xff0c;第三个参数表示比较的仿函数&#xff0c;用于对键值进行比较&#xff0c;默认情况下采用less<Key>…

Shell脚本攻略:shell实现pxe无人值守安装

目录 一、实验 1.shell实现pxe无人值守安装 一、实验 1.shell实现pxe无人值守安装 &#xff08;1&#xff09;脚本截图 1 echo set nu > ~/.vimrc2 systemctl stop firewalld3 setenforce 04 umount /dev/sr0 /mnt5 mount /dev/sr0 /mnt6 cd /etc/yum.repos.d/7 mkdir b…

汇编学习教程:bp 寄存器

引言 我们在此前的学习中已经了解了CPU中众多的寄存器&#xff0c;比如通用寄存器 AX、BX、CX、DX&#xff0c;还有段寄存器 CS、DS、SS、ES。在内存访问和灵活寻址的学习中&#xff0c;我们重点学习了 BX 寄存器和 CX 寄存器。BX 寄存器通常配合 DS段寄存器来实现内存访问&am…

做完瑞吉外卖项目的一点笔记和源码

源码在 https://gitee.com/pluto8/take-out 一、软件开发整体介绍 1、软件开发流程 需求分析 &#xff1a;产品原型&#xff0c;需求规格说明书&#xff08;文档形式&#xff09;设计&#xff1a;产品文档、UI界面设计、概要设计、详细设计、数据库设计编码&#xff1a;项目…

STM32 实现简单定时任务调度器,支持动态添加临时任务

代码实现和硬件没关系&#xff0c;所以并不限于STM32&#xff0c;Arduino 之类的其他地方也能用&#xff0c;只要有一个能获取时间的函数就行&#xff0c;或者说&#xff0c;只要有一个会随着时间自动增加的变量就行&#xff0c;时间单位无所谓&#xff0c;所以确实想的话&…

菜单权限验证和分页功能

权限验证 1.创建数据库&#xff0c;然后测试菜单权限的联合查询语句&#xff1b; 2.创建项目&#xff0c;导入jar包&#xff0c;配置实体类和工具类 3.完成登录功能&#xff0c;当输入用户名和密码正确后跳转到框架页面 编写导航页&#xff08;top.jsp&#xff09;和内容页…

Vscode +Msys2配置C/C++环境

目录 前期准备&#xff1a;Step1: 安装Msys2Step2: 安装编译器Step3: 安装VScodeStep4: 配置VScodec_cpp_properties.jsonlaunch.jsontasks.json Step5: 创建C/C项目 前期准备&#xff1a; 首先&#xff0c;你需要下载并安装以下软件&#xff1a; VsCode&#xff1a;https://c…

软件需求分析-复习指南

这里写自定义目录标题 下面是一段用例的描述&#xff0c;针对一个汽车保险系统中“将一辆新车加入一个已有保单中”的用例。请你为其设计&#xff1a; (1) 领域模型&#xff08;要求给出建立过程&#xff09;(20分)&#xff1b; (2) 活动图 (14分)&#xff1b; (3) 顺序图 (14分…

linux内核open文件流程

打开文件流程 本文基本Linux5.15 当应用层通过open api打开一个文件&#xff0c;内核中究竟如何处理&#xff1f; 本身用来描述内核中对应open 系统调用的处理流程。 数据结构 fdtable 一个进程可以打开很多文件&#xff0c; 内核用fdtable来管理这些文件。 include/linu…