常见面试-JVM-指针压缩

jvm 参数 +UseCompressedClassPointers java -XX:+PrintCommandLineFlags -version 注意:最大堆内存和最小堆内存一般设置为统一,避免它自动膨胀缩小浪费资源 //对象头指针的大小可以压缩 前两个 object header 为mardword 第三个为 class point 禁止压缩classpoint (从4byte 变 为 8byte) 前两个 object header 为mardwo... 阅读详情

了解指针压缩前,首先需要了解对象的内存布局结构

对象内存结构

在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:

1.对象头

Hotspot 虚拟机的对象头包括两部分信息:

第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等)

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

其实还有一个数组长度

对象是数组的情况下,才有这部分数据。对象不是数组,则没有这部分数据,不会为其分配空间。

2.实例数据(对象体)

 实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。

对象里的非静态属性占用的空间(包括父类的所有属性,不区分修饰类型),不包括方法,注意:是非静态属性,属于对象的属性,静态属性是属于类的不在对象上分配空间。如果属性是基本数据类型,则直接存对象本身,如果是引用类型,则存的是对象的指针。

3.对齐填充

对齐填充部分不是必然存在的,也没有什么特别的含义,仅仅起占位作用。 因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍。比如对象头+实例数据大小只有30字节,则需要补齐到32字节,换句话说就是对象的大小必须是 8 字节的整数倍。因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

指针压缩

计算机操作系统分32位和64位,即用32个(或64个)二进制0和1的组合来表示内存地址。

以32位为例,在普通的内存中,通过0和1的排列组合,能够表示寻址的内存空间最大就是2^32个,换算成内存空间就是2 ^ 32 / 1024 / 1024 / 1024 = 4G,也就是说32位的操作系统最大能寻址的内存空间只有4G,同理,64位的操作系统(查阅资料显示其实没有用到64位,最多只用到了48位,这个可自行查阅资料,反正肯定比32位大的多)2 ^ 48 / 1024 / 1024 / 1024 / 1024 = 256TB,这样内存就足够大了,但是目前还没有厂商能生产出这么大的内存。


4G对于现在的java应用系统来说,内存已经算小的了,那我们就会想到使用64位的系统,这样内存就可以更大了,但是当我们准备将32位系统切换到64位系统,起初我们可能会期望系统性能会立马得到提升,但现实情况可能并不是这样的,为什么呢?


(1)32位系统对象指针是4字节,64位系统对象指针是8字节(1位表示1bit,8个bit表示1字节),这样64位系统中的对象引用占用的内存空间是32位系统中的两倍大小,因此间接的导致了在64位系统中更多的内存消耗以及更频繁的GC发生,GC占用的CPU时间越多,那么我们的应用程序占用CPU的时间就越少,响应会变慢,吞吐量会降低。


(2)对象的引用变大了,那么CPU可缓存的对象相对就少了,降低了CPU缓存命中率,增加了对内存的访问,CPU对CPU缓存的访问速度可比对内存的访问速度快太多了,所以大量的对内存访问,会降低CPU的执行效率,增加了执行时间,从而影响性能。既然32位系统内存不够,64位内存够但又影响性能,那有没有折中方案来解决这两个问题呢,于是聪明的JVM开发者想到了利用压缩指针,在64位的操作系统中利用32位的对象指针引用获得超过4G的内存寻址空间。

如何压缩指针?

由于在JVM里,对象都是以8字节对齐的即对象的大小都是8的倍数,所以不管用32位还是64位的二进制表示,末尾3位始终都是0。

例如:8 = ...00001000, 16 = ...00010000,24 = ...00011000

既然JVM已经知道了这些对象的内存地址后三位始终是0,那么这些无意义的0就没必要在堆中继续存储。相反,我们可以利用存储0的这3位bit存储一些有意义的信息,这样我们就多出3位bit的寻址空间,也就是说如果我们继续使用32位来存储指针,只不过后三位原本用来存储0的bit现在被我们用来存放有意义的地址空间信息,当寻址的时候,JVM将这32位的对象引用左移3位即可(后三位补0)。我们原本32位的内存寻址空间一下变成了35位,可寻址的内存空间变为2 ^ 35 / 1024 / 1024 / 1024 = 32G,也就是说在64位系统JVM的内存可扩大到32G了,基本上可满足大部分应用的使用了。
所以在64位系统下,通过压缩指针我们可以继续使用32位来处理(引用指针由8字节可降低到4字节),存储的时候右移3位,寻址的时候左移3位,如下图所示。

这样一来,JVM虽然额外的执行了一些位运算但是极大的提高了寻址空间,并且将对象引用占用内存大小降低了一半,节省了大量空间,况且这些位运算对于CPU来说是非常容易且轻量的操作,可谓是一举两得。

常见数据大小

在64位机器上,

开启指针压缩(-XX:-UseCompressedOops)的情况下,对象头占用12bytes

不开启指针压缩(-XX:+UseCompressedOops)则占用16bytes

对象引用(reference)类型在64位机器上,关闭指针压缩时占用8bytes, 开启时占用4bytes。

例子:一个包含两个属性的对象:int和byte,并不是占用17bytes(12+4+1),而是占用24bytes(对17bytes进行8字节对齐)

数组对象的对象头占用24 bytes,启用压缩后占用16字节。比普通对象占用内存多是因为需要额外的空间存储数组的长度。

int[10]: 

  • 开启压缩:16 + 10 * 4 = 56 bytes;
  • 关闭压缩:24 + 10 * 4 = 64bytes。

如何进一步扩大寻址空间

前边提到在Java虚拟机堆中对象起始地址均需要对其至8的倍数,不过这个数值我们可以通过JVM参数-XX:ObjectAlignmentInBytes 来改变(默认值为8)。当然这个数值的必须是2的次幂,数值范围需要在8 - 256之间。正是因为对象地址对齐至8的倍数,才会多出3位bit让我们存储额外的地址信息,进而将4G的寻址空间提升至32G。
同样的道理,如果我们将ObjectAlignmentInBytes的数值设置为16呢?
对象地址均对齐至16的倍数,那么就会多出4位bit让我们存储额外的地址信息。

寻址空间变为2 ^ 36 / 1024 / 1024 / 1024 = 64G。
通过以上规律,我们就能知道,在64位系统中开启压缩指针的情况

寻址范围的计算公式:4G * ObjectAlignmentInBytes = 寻址范围


但是并不建议大家贸然这样做,因为增大了ObjectAlignmentInBytes虽然能扩大寻址范围,但是这同时也可能增加了对象之间的字节填充,导致压缩指针没有达到原本节省空间的效果。

JVM压缩指针参数

关于压缩指针的两个参数:

UseCompressedClassPointers:压缩类指针
UseCompressedOops:压缩普通对象指针
Oops是Ordinary object pointers的缩写,这两个参数默认是开启的,也可手动设置,如下所示

-XX:+UseCompressedClassPointers //开启压缩类指针
-XX:-UseCompressedClassPointers //关闭压缩类指针
-XX:+UseCompressedOops  //开启压缩普通对象指针
-XX:-UseCompressedOops  //关闭压缩普通对象指针

实例

使用 JOL 工具分析 Java 对象 

 maven依赖

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

常用方法:

查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable()
查看对象外部信息:GraphLayout.parseInstance(obj).toPrintable()
查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize()
查看类内部信息:ClassLayout.parseClass(Object.class).toPrintable() 

public class ObjectLayOut1 {
    public static void main(String[] args) {
        Goods goods = new Goods();
        goods.setAge((short) 10);
        goods.setNo(123456);
        goods.setId(111L);
        goods.setGoodsName("方便面");
        goods.setFlag(true);
        goods.setB((byte)1);
        goods.setPrice(1.5d);
        goods.setProduceTime(LocalDateTime.now());
        goods.setType('A');
        goods.setWeight(0.065f);
        System.out.println(ClassLayout.parseInstance(goods).toPrintable());
    }
 
}
 
@Setter
class Goods {
    private byte b;
    private char type;
    private short age;
    private int no;
    private float weight;
    private double price;
    private long id;
    private boolean flag;
    private String goodsName;
    private LocalDateTime produceTime;
}
com.star95.study.jvm.Goods object internals:
OFF  SZ                      TYPE DESCRIPTION               VALUE
  0   8                           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                           (object header: class)    0xf800c143
 12   4                       int Goods.no                  123456
 16   8                    double Goods.price               1.5
 24   8                      long Goods.id                  111
 32   4                     float Goods.weight              0.065
 36   2                      char Goods.type                A
 38   2                     short Goods.age                 10
 40   1                      byte Goods.b                   1
 41   1                   boolean Goods.flag                true
 42   2                           (alignment/padding gap)   
 44   4          java.lang.String Goods.goodsName           (object)
 48   4   java.time.LocalDateTime Goods.produceTime         (object)
 52   4                           (object alignment gap)    
Instance size: 56 bytes
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

 参考转载:https://www.cnblogs.com/star95/p/17512212.html

你知道哪些JVM性能调优?77道JVM系列面试题总结,彻底弄透JVM各种问题。 最新2020整理收集的一线互联网公司面试真题(都整理成文档),有很多干货,包含netty,spring,线程,spring cloud等详细讲解,也有详细的学习规划图,面试题整理等,我感觉在面试这块讲的非常清楚:获取面试资料只需:点击这里领取!!! 暗号:优快云 1、java中会存在内存泄漏吗,请简单描述。 会。自己实现堆载的数据结构时有可能会出现内存泄露。 2、64 位 JVM 中,int 的长度是多数? Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在. 阅读详情

相关推荐

JAVA面试之JVM篇

JVM定义了不同运行时数据区,他们是用来执行应用程序的。某些区域随着JVM启动及销毁,另外一 些区域的数据是线程性独立的,随着线程创建和销毁。jvm内存模型总体架构图如下:(摘自oracle 官方网站JVM在执行Java程序时,会把它管理的内存划分为若干个的区域,每个区域都有自己的用途和创建 销毁时间。如下图所示,可以分为两大部分,线程私有区和共享区。下图是根据自己理解画的一个 JVM内存模型架构图:JVM内存分为线程私有区和线程共享区Java虚拟机是一个可以执行Java字节码的虚拟机进程。

RE_Z002的博客 1003

详解JVM内存优化技术:压缩指针

当你提到Java虚拟机(JVM)的内存优化技术时,压缩指针是一项非常重要的技术。在本篇博文中,我们将详细介绍JVM的压缩指针内存优化技术,包括其概念、原理、作用以及可能产生的影响。

码到三十五 7570

深入理解JVM对象内存,盘服面试官,Offer稳了

Java对象首当其中,对象的大小,创建对象的多少,直接影响着内存。对象的估算,也是高并发情况下的对内存管理的一个重要指标。

static_coder的博客 863

JAVA对象头的指针压缩

JAVA对象头的指针压缩

有问题的请私信或关注v【技术印记】留言 1936

JVM面试篇

直接内存并不是 JVM 运行时数据区的一部分, 但也会被频繁的使用: 在 JDK 1.4 引入的 NIO 提供了基于 Channel 与 Buffer 的 IO 方式, 它可以使用 Native 函数库直接分配堆外内存, 然后使用DirectByteBuffer 对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 这样就避免了在 Java堆和 Native 堆中来回复制数据, 因此在一些场景中可以显著提高性能。

shuai_h的博客 1116

JVM面试题目与答案

JVM是Java运行基础,面试时一定会遇到JVM的有关问题,内容相对集中,但对只是深度要求较高。其中内存模型,类加载机制,GC是重点方面。性能调优部分更偏向应用,重点突出实践能力。编译器优化和执行模式部分偏向于理论基础,重点掌握知识点。需了解内存模型各部分作用,保存哪些数据。类加载双亲委派加载机制,常用加载器分别加载哪种类型的类。GC分代回收的思想和依据以及不同垃圾回收算法的回收思路和适合场景。性能调优常有JVM优化参数作用,参数调优的依据,常用的JVM分析工具能分析哪些问题以及使用方法。执行模式解释/编译

Firstlucky77的博客 2766

一文读懂JVM对象创建及内存分配机制上

各位小伙伴,当我们new一个对象的时候,对象到底是怎么生产出来的呢,我们这篇说一说对象生成的过程和内存的分配机制,面试的时候可以扯一扯,绝对是加分项。 1.加载类时检查 虚拟机在执行的过程中,执行到new关键字(new关键词、对象克隆、对象序列化等)的时候,第一步是先去检查这个指令的参数对应的符号引用是否在常量池中,其对应的类是否已经被加载解析和初始化,如果已经有,就代表此类已经被加载过了,如果吗,没有就说明类还没有被加载,那就要执行类记载的整个过程。 2.内存的分配 在类加载过程完成后,就要对新创建的对

斯巴达人的博客 515

JVM内存优化的秘密武器:压缩指针详解

🔥关注墨瑾轩,带你探索编程的奥秘!🚀🔥超萌技术攻略,轻松晋级编程高手🚀🔥技术宝库已备好,就等你来挖掘🚀🔥订阅墨瑾轩,智趣学习不孤单🚀🔥即刻启航,编程之旅更有趣🚀。

java专栏 1032

为什么JVM要用到压缩指针?Java对象要求8字节的整数倍?

把64位JVM的指针压缩为32位,即引入压缩指针的原因是为了节省内存,但其实64位JVM的指针本来就可以是64位。从8字节压缩到4字节,听起来好像才少了4个字节,但要知道,因为Java对象要补齐8字的倍数;假如一个Java对象刚好满足了8字节整数倍,但因为没有压缩指针多出来4字节,这时又因为要补齐,还需要再补上4字节,一个对象就多了8字节!听起来好像还不怎么多,但这可是一个对象就少8字节,如果是一个大项目,差的可能就不是一星半点了。

2401_83384536的博客 979

来聊聊大厂面试常问的指针压缩

先来说说第一个原因,默认情况下类型指针为8个字节,相较于指针压缩后的对象,它无缘无故就比后者多了4个字节,这意味着64位对象需要占用更多的堆空间,无意间加快的GC的发生,导致更频繁的。由上可知,我们的压缩后的指针记录地址都是8倍的地址值,这也就意味着只要我们的地址能够被8整除,那么元数据地址就可以被精准定位,所以上述。我们以64位操作系统为例,在未开启指针压缩的情况下,类型指针占用8字节,一旦开启指针压缩,类型指针就会由原来的8字节变为4字节。的概念之后,又一个问题诞生了,假设我们的其中一个对象的地址为。

禅与计算机程序设计的艺术 1097

机器人抓取(一)—— ROS MoveIt! 程序控制真实UR5机器人运动(python)

接上篇:ubuntu18.04 ros-melodic 安装 ur_robot_driver,驱动真实的 ur5 机器人 上文在 ubuntu 系统中配置了ur 机器人ros功能包 universal_robot 和驱动 Universal_Robots_ROS_Driver,并实现了用 moveit_rviz 拖动机器人运动。本文旨在学习如何使用 moveit python 用户接口moveit_commander,实现真实的ur5 机器人轨迹规划与控制。 1. ur5 机器人控制 参考:MoveIt »

zxxRobot的博客 1万+

JVM常见面试知识点

JDK、JRE、JVM

weixin_43364551的博客 1040

STM32+ESP8266接入OneNet的MQTT嵌入式实现

MQTT是一种轻量级发布/订阅模式的消息传输协议,广泛应用于资源受限的物联网终端设备通信。其基于TCP/IP栈构建,具备低带宽占用、小代码体积和高可靠性等技术优势,特别适合STM32等MCU与Wi-Fi模组协同工作的边缘节点场景。在实际工程中,需结合AT指令集控制ESP8266完成Wi-Fi连接、TCP建链及MQTT会话管理,并通过环形缓冲区、空闲中断检测等机制保障UART异步通信稳定性。本文围绕STM32F103与ESP8266硬件协同、OneNet平台对接、JSON消息解析与OLED状态反馈等关键环节,

yhn456789的博客 1037

JAVA面试题分享四百六十六:大厂面试常问的指针压缩

我们以64位操作系统为例,在未开启指针压缩的情况下,类型指针占用8字节,一旦开启指针压缩,类型指针就会由原来的8字节变为4字节。由上可知,我们的压缩后的指针记录地址都是8倍的地址值,这也就意味着只要我们的地址能够被8整除,那么元数据地址就可以被精准定位,所以上述。先来说说第一个原因,默认情况下类型指针为8个字节,相较于指针压缩后的对象,它无缘无故就比后者多了4个字节,这意味着64位对象需要占用更多的堆空间,无意间加快的GC的发生,导致更频繁的。的概念之后,又一个问题诞生了,假设我们的其中一个对象的地址为。

之乎者也·的博客 965

一次渗透测试引发的Json格式下CSRF攻击的探索

一次渗透测试引发的Json格式下CSRF攻击的探索

weixin_52501704的博客 1050

基于近场动力学的二维疲劳裂纹扩展模型_近场动力学数值模拟的程序实现(1)——引言...

参考书目W. H. Gerstle. 2015. Introduction to Practical Peridynamic: Computational Solid Mechanics Without Stress and Strain.E.Madenci, A.Barut, M.Dorduncu. Peridynamic Differential Operator for Numerical ...

weixin_39764603的博客 1202

为什么JVM开启指针压缩后支持的最大堆内存是32G?

作为Java程序员,当我们在命令行提交一个Java任务时,一般我们会显性的设置一些参数,最常见的如指定堆内存大小,程序参数等,来满足你程序的运行需求。但是,在设置JVM内存大小时,我建议你最好不要超过32G这个阈值,因为一旦超过,你的原本的内存占用可能会瞬间剧增。为什么?-XX:+UseCompressedClassPointers //压缩类指针-XX:+UseCompressedOops //压缩普通对象指针。

meser88的博客 1237

Netty UDP

UDP是一种无连接的协议,它并不在通信双方之间建立连接,而是直接将数据包从发送端发送到接收端。UDP不保证数据包的可靠传输,也不保证数据包的顺序,因此其传输速度通常比TCP快,且系统开销较小。然而,这也意味着UDP在某些情况下可能会丢失数据包或数据包到达的顺序错乱。

王小工小工历程 2027

面试 - JVM相关面试题

面试 - JVM相关面试题

qq_43350524的博客 1175

RV1126笔记二十七:MetaRTC6移植

MetaRTC6移植

6038

lammps 案例in文件汇总

进阶案例: 聚乙烯和石墨烯界面pull out模拟代码 反应力场reax/c设置方法及案例解析 石墨烯热导率模拟代码(NEMD方法) 圆锥形SiC纳米磨削单晶硅源代码 高熵合金FeNiCrCoCu纳米压痕模拟代码 fix indent命令实现纳米摩擦及摩擦力计算 Fe原子辐照轰击多层石墨烯模拟代码 铝基石墨烯复合物压缩变形in文件源代码 石墨烯自动卷曲到Fe纳米线模拟源代码 铝/石墨烯复合物结晶过程lammps模

lammps_jiayou的博客 9446

JVM面试宝典-对象指针压缩是怎么回事

什么是java对象的?开始,在64bit操作系统中,JVM支持指针压缩2.jvm配置参数压缩、对象指针3.启用指针压缩,禁止指针压缩。

weixin_50687701的博客 263

基于Simulink的BCH码编解码仿真建模示例

BCH码是一类循环码,能够纠正一定数量的随机错误。灵活性:可以根据需要选择不同的码长和纠错能力。高效性:适用于短码长和中等码长的场景,广泛用于卫星通信、存储系统等。实现简单:编码和解码过程可以通过硬件或软件高效实现。在本示例中,我们将使用MATLAB/Simulink中的和模块来完成BCH码的编解码操作,并通过仿真验证其误码率(BER)性能。通过本指南,我们介绍了如何基于Simulink搭建一个BCH码的编解码模型,并进行了仿真以验证其性能。背景介绍:理解BCH码的基本原理及其在通信系统中的应用。

xiaoheshang_123的博客 191

JVM底层原理常见知识点&面试题总结

秋招后端开发面试题 - JVM底层原理

栗子酱的Blog 1268

WINCC7.3破解文件

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

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

java 对象压缩_《Java 底层原理》Jvm对象结构和指针压缩

前言Java 程序调优是一个程序员必备的技能。对象内存结构对象头:Mark Word :32bit机 4B;64bit机 8B 是固定的。类型指针:klass pointer,引用类型在方法区的地址。 开启指针压缩占4字节,不开启占8个字节。数组长度:对象不是数组占0字节,对象是数组占4字节,开启指针压缩,数组长度会放到类型指针的后半段。对其填充:在关闭指令压缩的情况下,数组类型还会进行填充,称为...

weixin_34150870的博客 338

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

上一篇: 整合ssm,org.springframework.beans.factory.BeanInitializationException: Could not load properties; ne
下一篇: Redis哨兵集群
Truth-Liam
博客等级 码龄8年 53粉丝 8原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值