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

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


来源  |  阿里云开发者
作者  |  
毛亮

相关文章
|
1月前
|
人工智能 安全 Java
Java和Python在企业中的应用情况
Java和Python在企业中的应用情况
52 7
|
1月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
144 3
|
2天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
18 2
|
25天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
103 6
|
23天前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
29 2
|
1月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
54 6
|
1月前
|
关系型数据库 MySQL Java
MySQL索引优化与Java应用实践
【11月更文挑战第25天】在大数据量和高并发的业务场景下,MySQL数据库的索引优化是提升查询性能的关键。本文将深入探讨MySQL索引的多种类型、优化策略及其在Java应用中的实践,通过历史背景、业务场景、底层原理的介绍,并结合Java示例代码,帮助Java架构师更好地理解并应用这些技术。
31 2
|
1月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
95 4
|
1月前
|
Java BI API
Java Excel报表生成:JXLS库的高效应用
在Java应用开发中,经常需要将数据导出到Excel文件中,以便于数据的分析和共享。JXLS库是一个强大的工具,它基于Apache POI,提供了一种简单而高效的方式来生成Excel报表。本文将详细介绍JXLS库的使用方法和技巧,帮助你快速掌握Java中的Excel导出功能。
70 6