Java对象头压缩---- 永久为Java应用“降本增效”

简介: 本文介绍了一下OpenJDK的最新技术,对象头压缩,来大幅优化Java对象的内存占用。

前言


Java丰富的生态和语言强大的内存管理技术(GC),使得Java应用的开发非常便捷,各类应用场景的适配都非常优秀,大大减少了从Idea到应用落地的难度。不过这一切也不是没有代价的,针对于Java应用内存占用比较高的问题一直拿出来和其他语言比较。虽然JVM已经自带了例如指针压缩(compressed oops)来节约内存开销,不过Java Object对象头本身占用的内存还是非常可观。本文就介绍一下OpenJDK的最新技术,对象头压缩,来大幅优化Java对象的内存占用。目前这个技术尚未在Java语言官方实现OpenJDK中正式发布,但是Dragonwell11已经率先应用,请参考JDK发布指南:Dragonwell 11 release notes[1]


Java对象头压缩 JEP 450: Compact Object Headers


Java对象头压缩技术,已经创建官方的JEP[2]。JEP源自Shenandoah GC的project lead Roman Kenne创建的Lilliput:https://openjdk.org/projects/lilliput[3]。(Lilliput意为《格列佛游记》中的小人国,含义不言而喻)


Java的对象布局

我们以基础类型java.lang.long为例


image.png

image.png

Compact object headers的核心逻辑是将单独的narrow klass指针(压缩class指针)encode在基础的对象头的第一个word:mark word中,释放原先单独占用一个word的narrow-klass/klass指针的空间,从而实现对Java对象头整体的压缩和内存占用优化。我们可以看到java.lang.Long对象应用对象头压缩后,内存占用从24 bytes减少到16 bytes,减少了1/3。由于原始layout的java对象头需要支持biased locking以及CMS GC,需要预留更多的unused bits,所以使用对象头压缩无法支持biased locking以及CMS。(注:biased locking在JDK17中默认关闭,JDK21+中删除,CMS GC在JDK17+中删除)


“Early adopters of Project Lilliput who have tried it with real-world applications confirm that live data is typically reduced by 10%–20%.”


JEP中提到,早期的项目试用者在实际应用中发现内存占用下降10-20%。


JEP 450: Compact Object Headers依赖的主要实现


JDK-8291555[4]


使用了一个stack locking的替换方案,代替了原先的stack locking。原先的stack locking需要将object mark word交换到stack上,而产生remote object header的情况,压缩对象头后,频繁lock/unlock会无法稳定获取klass指针。


JDK-8305896[5]

在G1等的Full GC中,object mark word会用来保存forward oop,从而在GC过程中,将无法正确获取class指针,因此,需要有额外的机制来保存forward oop。


JDK-8305898[6]

在G1/Parallel GC等的gc过程中,如果出现evacuation failure,将产生self forwarding的情况,对象头会用来存放自身对象指针(oop),也会引起class指针无法读取,因此需要换一种方法,使用self forward bit来标注java对象(oop)在gc过程中是否self forwarding。


JDK-8305895[7]

JEP450主体实现。完成了由+/-UseCompactObjectHeaders控制的对象头压缩的实现。


JDK-8328138(阿里巴巴Propose)[8]

修复了因为对象头压缩引起的ARM服务器Arrays.equals的潜在crash问题,并提升ARM服务器 Arrays.equals的性能。


UseCompactObjectHeaders实测效果


Dragonwell JDK的UseCompactObjectHeaders,进行了完善JDK回归测试,并在多个主流核心场景中进行了验证,主要有如下几个典型的优势:

1. Java对象内存占用减少5-10%左右

2. Java新分配对象内存占用(allocation rate)和GC频率降低5-10%左右

3. CPU和基础吞吐性能基本保持一致,部分内存带宽使用较高的场景中,显著提升吞吐性能(例如SPECjbb2015和Flink)


SPECjbb2015

在SPECjbb2015基准测试中(倚天平台),max(极限吞吐)和critical(低延迟要求吞吐)分别提升6.17%和9.01%。

image.png

Flink大数据

Flink的基准benchmark nexmark在倚天平台下,平均吞吐提升约10%。阿里云的客户实测得到近似的优化效果。


image.png


淘宝天猫电商核心应用

淘宝天猫电商核心系统,实测G1 young GC频率降低约7%。

image.png

对象头压缩Compact Object Headers FAQ

1. 如何使用对象头压缩功能?

在支持该功能的Dragonwell 11的JDK版本中,增加启动参数-XX:+UseCompactObjectHeaders,仅支持-XX:+UseG1GC(默认)和-XX:+UseParallelGC

2.为何OpenJDK官方未发布的技术,已经在Dragonwell 11中发布,使用会有什么潜在风险吗?

目前的Compact object headers的实现无法支持ZGC,ZGC支持依赖的JDK-8315884的实现尚未完成。同时Lilliput在Object header上的改动关系到Java未来重点项目Valhala project(Value Object)对Object header的定义,还没有明确定论。因此OpenJDK Compact object headers的正式发布还没有确切时间表,并非受制于本身技术实现。不过这并不影响我们在当前的JDK LTS版本中落地该技术,Dragonwell 11在支持Compact object headers时,仅支持最常用的默认G1 GC以及Parallel GC。目前在阿里内部各类场景中大规模使用,均未发现风险。


参考链接:

[1]https://github.com/dragonwell-project/dragonwell11/wiki/阿里巴巴Dragonwell11-Extended发布说明[2]https://openjdk.org/jeps/450
[3]https://openjdk.org/projects/lilliput[4]https://bugs.openjdk.org/browse/JDK-8291555[5]https://bugs.openjdk.org/browse/JDK-8305896[6]https://bugs.openjdk.org/browse/JDK-8305898[7]https://bugs.openjdk.org/browse/JDK-8305895

[8]https://bugs.openjdk.org/browse/JDK-8328138


来源|阿里云开发者公众号

作者|毛亮

相关文章
|
2月前
|
人工智能 安全 Java
Java和Python在企业中的应用情况
Java和Python在企业中的应用情况
83 7
|
2月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
209 3
|
11天前
|
Java
java代码优化:判断内聚到实体对象中和构造上下文对象传递参数
通过两个常见的java后端实例场景探讨代码优化,代码不是优化出来的,而是设计出来的,我们永远不可能有专门的时间去做代码优化,优化和设计在平时
29 15
|
27天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
62 2
|
2月前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
269 12
基于开源框架Spring AI Alibaba快速构建Java应用
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
233 6
|
1月前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
45 2
|
2月前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
85 7
|
2月前
|
关系型数据库 MySQL Java
MySQL索引优化与Java应用实践
【11月更文挑战第25天】在大数据量和高并发的业务场景下,MySQL数据库的索引优化是提升查询性能的关键。本文将深入探讨MySQL索引的多种类型、优化策略及其在Java应用中的实践,通过历史背景、业务场景、底层原理的介绍,并结合Java示例代码,帮助Java架构师更好地理解并应用这些技术。
80 2