0

0

OpenJDK 15+ 内存优化:深入理解大堆场景下的压缩类指针

霞舞

霞舞

发布时间:2025-09-03 13:10:01

|

1019人浏览过

|

来源于php中文网

原创

OpenJDK 15+ 内存优化:深入理解大堆场景下的压缩类指针

Java 15及更高版本在处理超过32GB的大堆内存时,通过独立压缩类指针(Compressed Class Pointers)显著优化了对象的内存占用。这一改进使得即使对象引用本身无法压缩,对象的元数据开销也能保持较低水平,从而提升了内存效率,解决了早期Java版本中压缩类指针与压缩对象指针绑定导致的内存膨胀问题。本文将深入探讨这一机制,解释为何在OpenJDK 19中,即使面对大堆内存,Java对象的内存占用依然能够保持较低水平。

理解Java中的指针压缩机制

java虚拟机(jvm)中,为了节省内存并提高缓存效率,引入了指针压缩(compressed pointers)机制。它主要分为两类:

  1. 压缩对象指针 (Compressed Ordinary Object Pointers, Compressed Oops): 当Java堆内存大小在一定范围内(通常是32GB以下)时,JVM可以将64位对象引用(8字节)压缩为32位(4字节)。这是通过将对象地址视为相对于堆基址的偏移量,并假设对象总是8字节对齐来实现的。这样,一个32位的偏移量可以表示高达2^35字节(32GB)的地址空间。当堆内存超过32GB时,Compressed Oops 通常会自动禁用,对象引用会恢复为8字节。

  2. 压缩类指针 (Compressed Class Pointers): 每个Java对象在内存中都包含一个指向其Class对象的指针。这个指针指向了该对象的类型信息,是对象头的一部分。与Compressed Oops类似,Compressed Class Pointers 机制旨在将这个Class指针从8字节压缩为4字节,从而进一步减少每个对象的内存开销。

Java 11与Java 19大堆内存表现差异的根源

用户可能会观察到,在Java 11中,当堆内存超过32GB时,对象的内存占用会显著增加,而在Java 19中,即使堆内存达到41GB,某些对象的内存占用却依然较低,这似乎与Compressed Oops的32GB限制相矛盾。这种差异的根源不在于对象引用(Oops)本身的压缩,而在于类指针(Class Pointer)的压缩行为

在JDK 15之前,UseCompressedClassPointers 这个JVM选项与 UseCompressedOops 选项是紧密绑定的。这意味着:

  • 当堆内存小于32GB,UseCompressedOops 默认启用时,UseCompressedClassPointers 也会随之启用,类指针被压缩为4字节。
  • 当堆内存大于32GB,UseCompressedOops 自动禁用时,UseCompressedClassPointers 也会被隐式禁用,导致类指针恢复为8字节。

因此,在JDK 11(早于JDK 15)中,当堆内存设置为41GB时,不仅对象引用会是8字节,每个对象的类指针也会是8字节,从而增加了对象的整体内存开销。

JDK 15及之后的改进:类指针的独立压缩

OpenJDK社区通过 JDK-8241825: Make compressed oops and compressed class pointers independent 这一改进,解决了上述绑定问题。自JDK 15起,UseCompressedClassPointers 选项变得独立于 UseCompressedOops。这意味着:

  • 即使堆内存超过32GB,导致 UseCompressedOops 禁用(对象引用为8字节),UseCompressedClassPointers 仍然可以保持启用状态。
  • 因此,即使在大堆场景下,对象的类指针依然可以被压缩为4字节。

这个改变显著降低了每个Java对象的元数据开销,即使在无法压缩对象引用的大堆内存环境中,也能实现内存效率的提升。

示例分析:验证类指针的压缩效果

为了直观地展示这一变化,我们可以使用 Java Object Layout (JOL) 工具来分析对象的内存布局。以下代码片段展示了如何打印一个 Object 数组的内存布局:

import org.openjdk.jol.info.ClassLayout;

public class ObjectMemoryLayout {
    public static void main(String[] args) {
        // 打印一个包含3个Object引用的数组的内存布局
        System.out.println(ClassLayout.parseInstance(new Object[3]).toPrintable());
    }
}

使用上述代码,并在不同JDK版本和堆大小下运行,我们可以观察到以下差异:

1. JDK 11 (或更早版本,堆大小 41GB)

在JDK 11环境下,使用 -Xms41g -Xmx41g 运行,Object 数组的内存布局可能类似如下:

聚蜂消防BeesFPD
聚蜂消防BeesFPD

关注消防领域的智慧云平台

下载
[Ljava.lang.Object; object internals:
OFF  SZ               TYPE DESCRIPTION               VALUE
  0   8                    (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8                    (object header: class)    0x000001f54bec41e0  <-- 注意这里是8字节
 16   4                    (array length)            3
 20   4                    (alignment/padding gap)
 24  24   java.lang.Object Object;.        N/A
Instance size: 48 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

可以看到,object header: class 字段占用了8字节,导致整个 Object[3] 实例的大小为48字节。这是因为 UseCompressedClassPointers 随 UseCompressedOops 一同被禁用。

2. JDK 15 或更新版本 (例如 JDK 19,堆大小 41GB)

在JDK 15或更高版本(如JDK 19)环境下,使用 -Xms41g -Xmx41g 运行,Object 数组的内存布局可能类似如下:

[Ljava.lang.Object; object internals:
OFF  SZ               TYPE DESCRIPTION               VALUE
  0   8                    (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                    (object header: class)    0x000020fc          <-- 注意这里是4字节
 12   4                    (array length)            3
 16  24   java.lang.Object Object;.        N/A
Instance size: 40 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

在此输出中,object header: class 字段仅占用4字节。由于类指针的压缩,以及相应的填充调整,整个 Object[3] 实例的大小减少到了40字节。值得注意的是,Object;. 部分(即数组中存储的3个 Object 引用)在两种情况下都占用了24字节(每个引用8字节,因为 UseCompressedOops 已禁用),这进一步证明了差异在于类指针,而非对象引用本身。

结论与最佳实践

从JDK 15开始,Java虚拟机在处理大堆内存时,通过使压缩类指针独立于压缩对象指针,显著优化了对象的内存占用。这意味着即使您的应用程序需要超过32GB的堆内存,并且无法利用Compressed Oops来压缩对象引用,JVM仍然可以通过压缩类指针来减少每个对象的固定开销。

主要启示:

  • 区分概念: 理解“压缩对象指针”(Compressed Oops)和“压缩类指针”(Compressed Class Pointers)是两个独立但相关的优化。
  • JDK 15+ 内存优势: 对于运行在大堆内存环境下的Java应用程序,升级到JDK 15或更高版本可以带来潜在的内存效率提升,减少对象元数据开销。
  • 工具辅助分析: 使用如JOL (Java Object Layout) 这样的工具是分析和理解Java对象内存布局的强大手段,有助于开发者深入探究内存使用情况并进行优化。

通过利用这些JVM内部的优化,开发者可以更好地管理和利用系统资源,尤其是在处理大规模数据和高并发场景下的Java应用程序时。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

0

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号