写点什么

JEP 192(String Deduplication in G1)简介

  • 2014-03-25
  • 本文字数:1841 字

    阅读完需:约 6 分钟

JEP,即 JDK Enhancement Proposals,指的是为增强 JDK 而引入的一些提案,比如 Nashorn JavaScript 引擎就是在 JEP 174 中提出的。2013 年 11 月 22 日,来自 Oracle 的 Per Liden 创建了 JEP 192(String Deduplication in G1),意在增强 G1 垃圾收集器,去掉堆中重复的 String 对象,从而减少堆内存的占用。该文档近日又有更新,Per Liden 也提交了相应代码实现,目前还处于审校和讨论中。

应该注意的是该特性只针对 G1 垃圾收集器,不适用于其他收集器。

很多大规模 Java 应用都受限于内存瓶颈,测量表明,这类应用中,String 对象大概占了 Java 堆中活数据集的 25%。而这些 String 对象中,又大约有一半是重复的,这里的重复是指,对两个字符串 string1 和 string2 而言,string1.equals(string2) 为 true。存在重复的 String 对象本质上是对内存的浪费。JEP 192 打算在 G1 收集器中实现自动和连续的 String 去重操作,以避免内存浪费,进而减少总的内存占用量。

目前 String 类有两个字段:

private final char[] value;

private int hash;

使用旧版本 Java 的读者可能有点诧异,其实早期版本中的 count、offset 等实例字段已经去掉了,InfoQ 之前也曾报道过。

value 字段是特定于实现的,在 String 类之外看不到。因为 String 类不会修改该数组的内容,也不会将其用于同步,所以我们可以安全且透明地将其在多个 String 对象之间共享。也就是说我们可以将一个 String 对象的 value 指向另一个 String 对象的 value。尽管该字段是 final 的,但因为去重操作是在虚拟机内部实现的,所以这不是问题。有兴趣的读者可以查看一下 java.lang.System 类的实现,其中的

public final static InputStream in = null;

一句,就是先将 final 字段 in 设置为 null,然后在 native 代码中重新赋值的。

这里需要注意的是,实现并没有真的去掉重复的 String 对象,去掉的只是对象中的 char 数组。这样对应用才是透明的。去掉实际的 String 对象并不安全,因为应用可能将该对象用于同步等操作。这种实现不需要修改 JDK 类库或其他任何现有的 Java 代码。

Per Liden 对大量大大小小的 Java 应用进行了测量,发现了下列结果:

  • String 对象平均占活数据的 25%
  • 重复的 String 对象平均占活数据的 13.5%
  • String 的平均长度为 45 个字符

经过分析计算,通过去重、复用 char 数组,平均大概能减少 10% 的堆内存占用。

JEP 192 文档中介绍了实现思路。垃圾收集执行时会访问堆上的活对象,在访问对象时可以判断一下该对象是否可以作为字符串去重的候选。如果是,将其插入一个队列。有一个负责去重的线程在后台运行,处理该队列。使用一个哈希表来记录 String 对象使用的所有唯一的 char 数组(即 value)。在处理候选的 String 对象时,先查找哈希表,看是不是存在和当前处理对象内容相同的 char 数组。如果存在,则更新当前对象的 value 值,使其指向在哈希表中找到的 char 数组,这样垃圾收集器就可以在某个时间把当前对象原来的 char 数组回收掉了。如果不存在,则将当前对象的 char 数组插到哈希表中,供以后处理。对于哈希表中的某个 char 数组,如果引用它的所有对象都已经不可达了,即可将其移除。该哈希表会根据当前表项的数目动态调整,使用链表处理冲突。

这里有一个重要的参数:去重年龄阈值。对象的存活时间长短不一。对于存活时间很短的对象,执行去重操作其实是浪费资源。为避免这种情况,可以设置一个年龄阈值。在 String 对象的年龄等于该阈值时,才考虑对其进行去重操作,大于该阈值则是已经处理过的。该阈值应该提供一个合理的默认值,同时支持通过虚拟机选项来配置。

实际的去重操作在去重线程中完成。它会等待 String 对象引用出现在去重队列中,然后一个一个地将其从队列中去掉。在去掉时进行处理,计算字符串的哈希值,在哈希表中查找,如果可能的话执行去重操作。去重线程负责维护一些统计信息(已检查的候选对象数,去重的字符串数等),这些信息可以打印到 GC 日志中。

需要提供新的虚拟机命令行选项:

  • UseStringDeduplication (bool) ——支持字符串去重
  • PrintStringDeduplicationStatistics (bool) ——打印详细的去重统计信息
  • StringDeduplicationAgeThreshold (uintx) ——设置 String 对象的年龄阈值

文档中还对比评价了其他方案存在的一些问题,感兴趣的读者可以参考。


感谢张龙对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-03-25 23:332608
用户头像
臧秀涛 略懂技术的运营同学。

发布了 300 篇内容, 共 143.7 次阅读, 收获喜欢 35 次。

关注

评论

发布
暂无评论
发现更多内容

Linux云计算架构师:Linux全套实战学习资料

侠盗安全

Linux linux运维 运维工程师 云计算架构师 linux电子书

对话中国人寿:DevOps实践经验分享

龙智—DevSecOps解决方案

DevOps 中国人寿

详解布隆过滤器的原理和实现

万俊峰Kevin

golang 微服务 微服务架构 布隆过滤器 go-zero

企业管理升级,如何加速信息化转型?

雯雯写代码

企业管理 信息化

2021年财经中国年会暨第十九届中国财经风云榜

大咖说

直播 财经 财经峰会

学python,怎么能不学习scrapy呢,这篇博客带你学会它

梦想橡皮擦

12月日更

喜报 | 旺链科技获批张江国家自主创新示范区专项发展资金!

旺链科技

区块链 数字经济 产业区块链

元气部落盲盒芒趣一番赏盲盒app开发搭建

风行无疆

鸿蒙智联设备开发,这五大法宝你应该拥有|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

信息网络向价值网络演进过程中产品形态的思考

拍乐云Pano

给弟弟的信第5封|从高中到大学的体会

大菠萝

28天写作

华云大咖说 | 华云校园信创云解决方案

华云数据

华云数据

WebView秒开方案探索

得物技术

CSS JavaScript html webview 大前端

全网最全-混合精度训练原理

科技热闻

直播预告丨“Hello Ability:从页面跳转开始”周三晚不见不散

HarmonyOS开发者

HarmonyOS

百度文库新一代文档阅读器!核心技术点全解析!

百度Geek说

大前端 文档 百度文库

测试 —— DevOps 快速交付的最大瓶颈

飞算JavaAI开发助手

kubernetes系列随笔03:kubernetes的发展和设计思想

Geek_cd6rkj

Docker Kubernetes 声明式

字节码引用检测原理与实战

vivo互联网技术

编程语言 字节码编程 引用

Android C++系列:Linux线程(四)线程同步

轻口味

android 28天写作 12月日更

高并发线下沙龙不容错过!

Qunar技术沙龙

高并发

关于RocketMQ事务方面Demo

前端开发JS框架之jQuery的基础知识分享

@零度

大前端 ​jQuery

网络协议之:基于UDP的高速数据传输协议UDT

程序那些事

TCP 网络协议 udp 程序那些事 12月日更

Java开发框架Struts相关知识分享

@零度

struts JAVA开发

更好用的Web端H265播放技术架构

百度开发者中心

智能视频

大数据开发之Kafka 存储选型

@零度

大数据 kafka

聊聊Java底层那些事

码农参上

内容合集 签约计划第二季 技术专题合集 技术专区合集

全网最全-混合精度训练原理

科技热闻

【量化】资产组合理论:鸡蛋不能放在一个篮子里

恒生LIGHT云社区

量化投资 量化

【Promise 源码学习】第十四篇 - 实现工具方法 promisify

Brave

源码 Promise 12月日更

JEP 192(String Deduplication in G1)简介_Java_臧秀涛_InfoQ精选文章