JEP解读与尝鲜系列4 - Java 16 中对于 Project Valhalla 的铺垫
这是 JEP 解读与尝鲜系列的第 4 篇,之前的文章如下:
在系列之前的第一篇文章 - JEP 解读与尝鲜系列 1 - Java Valhalla 与 Java Inline class 中,我介绍了 Project Valhalla 项目中的核心 Java Inline Class,总结起来其实就是 Java 中的值类型。Java 中目前只有类对象,没有值类型的对象。普通的类对象有对象头,因此这种对象可以用来做同步锁,可以使用它的 wait() notify() 等方法实现阻塞同步,同时这些对象需要在堆上面分配,通过 JVM GC 进行内存回收。并且这种对象的数组,只有数组本身是内存连续的,上面引用的对象并不是:

Project Valhalla 提出并设计实现了 Java 的值类型,去掉了对象头,只存储它其中的值。这样减少了这种对象占用的空间,但是也让这种对象无法使用对象的 sychronization 同步,同时也失去了 对于wait() notify()这些方法的支持。同时这种对象期望是可以直接在栈上直接分配的,不用像普通对象一样需要在堆上分配,和原始类型例如 int 一样。同时这种对象的数组,期望在内存中数组的每个对象内存都是连续的:

这样也节省了指针的存储空间。
但是这些目前还是在设计实现中,并不是最终的实现模型,但是可以看出其中的趋势。
为了能使 Project Valhalla 最终落地实现,我们先要对 JDK 的一些元素做兼容。
JDK 中的哪些类和值类型相关
首先,最先想到的就是 Java 的原始类型对应的封装类型,例如 java.lang.Integer。原始类型是可以也是需要改造成 Java 值类型的,但是需要避免项目中使用了 Integer 对象的 wait() notify(), notifyAll() 方法,或者将这个作为 synchronization 的对象。
然后想到的就是原子类,例如 java.util.concurrent.atomic.AtomicInteger。其实在 Java 9 之后的 JMM 模型中实现了更细粒度的访问控制,例如:
private int locked = 0;
private static final VarHandle LOCKED;
//操作 locked 的句柄
static {
try {
//初始化句柄
LOCKED = MethodHandles.lookup().findVarHandle(当前类.class, "locked", int.class);
} catch (Exception e) {
throw new Error(e);
}
}
//原子操作
LOCKED.compareAndSet(this, 1, 0);
LOCKED.weakCompareAndSet(this, 1, 0);
LOCKED.weakCompareAndSetAcquire(this, 1, 0);
LOCKED.weakCompareAndSetPlain(this, 1, 0);
LOCKED.weakCompareAndSetRelease(this, 1, 0);
然后还有在 Java 11 的官方文档中提出的 Value-based Classes,参考:Java
11 Value-based Classes,Java 11 中的定义是:像是 java.util.Optional 和 java.time.LocalDateTime 这种类就是 Value-based Classes,这种类的实例:
- 本身是不可变的,虽然内部的值引用指向的是一个可变对象
- 实现了
equals,hashCode和toString方法,并且基于它包含的值实现,而不是基于他的 identity (例如对象基址)并且也不是基于其他对象的状态。 - 不会使用 identity-sensitive 的操作,例如通过
==对比两个实例的相等,使用默认的基于对象基址的 hashcode 实现(例如调用System.identityHashCode(对象)),以及作为 synchronization 的对象 - 只通过
equals对比对象相等,而不是== - 没有可访问的构造函数,而是通过工厂方法实例化,这些方法对返回的实例的 identity 不做任何保证,即这个返回对象的地址我们无法通过对于工厂方法的传参确定;
equals相等的两个对象,需要有完全相同的行为
这种 Value-based Classes 其实就与 Java 值类型的特征非常一致。于是,从 Java 16 开始,将 Value-based Classes 的定义进行了扩展,并且对它们的使用进行了报警限制,提示未来这些类型,不再使用普通类实现,而是使用 Project Valhalla 的 Java 值类型实现。
JEP 390: Warnings for Value-Based Classes
在 Java 16 中,为了给 Project Valhalla 的这一特性进行铺路,引入了一个 JEP:JEP 390: Warnings for Value-Based Classes
在最新的 Value-based Classes 的定义中(参考:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/doc-files/ValueBased.html ),将原始类型的封装类,例如 java.lang.Integer 也纳入了这一类的定义范畴。并在此基础上,增加两个说明:
1.非常不建议使用这一类的对象作为同步参数,例如 synchronize(obj),无法保证这个锁拥有者是谁以及是否是独占的。
这个问题倒不是因为以后要换值类型无法同步导致的,而是容易犯这种编程失误:
Integer i = 1;
for (int j = 0; j < 10; j++) {
synchronized(i) {
i++; //下次循环就变成另一个对象了,没有真正按照预期锁住
}
}
2.使用 identity 相关的操作可能未来会发生变化,所以不建议使用,例如:
- 调用
System.identityHashCode(对象)获取基于对象在堆内存地址实现的哈希码,如果 Value-based Classes 变成值类型,值类型确定在栈上分配后,这个方法目前的机制就会有问题。 - 调用
synchronize(obj)同步对象,如果 Value-based Classes 变成值类型,没有普通对象的对象头,那么无法使用正常的锁膨胀同步机制,同时重量锁 mutex 由于可能值类型对象没有堆上位置也无法使用现有的机制实现。 - 调用对象的
wait(),notify(),notifyAll(),由于上一条同样的影响,这些方法调用可能在未来版本带来异常。
在 Java 16 之后,如果有这些用法,就会在编译阶段有报警提醒:
Integer integer = 1;
synchronized (integer) {
}
编译阶段会提示 Attempt to synchronize on an instance of a value-based class ,如果想关闭可以增加编译参数 -Xlint:synchronization
如果想在运行阶段针对这种使用有提示或者错误,可以通过添加如下启动参数实现:
-XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=1:加上这个,程序遇到这种使用,会抛出 FATAL ERROR,同时退出 JVM-XX:+UnlockDiagnosticVMOptions -XX:DiagnoseSyncOnValueBasedClasses=2:加上这个,程序遇到这种使用,会有日志提示:
[0.152s][info][valuebasedclasses] Synchronizing on object 0x000000069aed7788 of klass java.lang.Integer
[0.152s][info][valuebasedclasses] at com.github.hashjang.shenandoah.Main.main(Main.java:8)
[0.152s][info][valuebasedclasses] - locked <0x000000069aed7788> (a java.lang.Integer)
同样的,由于原始类型包装类已经属于 Value-based Class,所以就不应该使用它的构造器而是使用 valueOf() 代替了,为了给大家修改的时间,目前仅仅是将构造器标记为 Deprecate for Removal :
@Deprecated(since="9", forRemoval = true)
public Integer(int value) {
this.value = value;
}
如果有使用会提示 'Integer(int)' is deprecated and marked for removal。
目前 JDK 中的未来可能会用值类型代替的 Value-based Classes
目前 JDK 中的 Value-based Classes 都带有 jdk.internal.ValueBased 注解,或者他们的实现接口,父类带有这个注解,包括:
java.lang包:- 原始类型的封装类,例如
java.lang.Integer java.lang.Runtime.Version类- 操作系统进程的句柄
java.lang.ProcessHandle和他的实现类java.lang.ProcessHandleImpl
- 原始类型的封装类,例如
java.time包下的一些时间封装类java.util包:- Optional 相关,例如:
java.util.Optional,java.util.OptionalInt,java.util.OptionalLong,java.util.OptionalDouble - 所有不可变集合以及底层实现的不可变元素,例如:
Set.of的返回java.util.ImmutableCollections.AbstractImmutableSet
- Optional 相关,例如:
一点趣事儿
Java 16 的 Record 还让我闹了个笑话,我以为这个是 Project Valhala 的 Inline Object 已经实现了,还去 StackOverflow 问,这个 Record 为啥能有 wait() 方法,并且可以进行 synchronized 同步(因为如果是 Project Valhala 的 Inline Object 的话是没有普通类的对象头的,没法用普通类对象的方法实现同步),结果。。。。。最后还是 Goetz 大佬一眼就看出我是误会了:

微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:

JEP解读与尝鲜系列4 - Java 16 中对于 Project Valhalla 的铺垫的更多相关文章
- JEP 尝鲜系列 3 - 使用虚线程进行同步网络 IO 的不阻塞原理
相关 JEP: JEP 353 Reimplement the Legacy Socket API JEP 373 Reimplement the Legacy DatagramSocket API ...
- Java 16 中新增的 Stream 接口的一些思考
这里先提一个题外话,如果想看 JDK 不同版本之间有何差异,增加或者删除了哪些 API,可以通过下面这个链接查看: https://javaalmanac.io/jdk/17/apidiff/11/ ...
- 关于java项目中的.project文件:
.project是项目文件,项目的结构都在其中定义,比如lib的位置,src的位置,classes的位置
- Java 16 新功能介绍
点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 程序猿阿朗博客 已经收录,有很多知识点和系列文章. Ja ...
- 官方正式发布 Java 16
前言 就在2021/03/16,官方正式发布了Java 16.我们可以下载使用Java 16了. 特性 向量API(孵化) 在运行期,Vector 表示向量计算可以可靠地编译成支持CPU架构上的最佳矢 ...
- Java 16 新特性:instanceof增强
instanceof这个关键词,主要用来判断某个对象是不是某个类的实例. 比如,有时候我们要处理一个类似这样的数据集: Map<String, Object> data = new Has ...
- Java 17中对switch的模式匹配增强
还记得Java 16中的instanceof增强 吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); d ...
- 实战派 | Java项目中玩转Redis6.0客户端缓存!
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 哈喽大家好啊,我是Hydra. 在前面的文章中,我们介绍了Redis6.0中的新特性客户端缓存client-side caching,通过tel ...
- Java协程编程之Loom项目尝鲜
前提 之前很长一段时间关注JDK协程库的开发进度,但是前一段时间比较忙很少去查看OpenJDK官网的内容.Java协程项目Loom(因为项目还在开发阶段,OpenJDK给出的官网https://ope ...
随机推荐
- canal整合springboot实现mysql数据实时同步到redis
业务场景: 项目里需要频繁的查询mysql导致mysql的压力太大,此时考虑从内存型数据库redis里查询,但是管理平台里会较为频繁的修改增加mysql里的数据 问题来了: 如何才能保证mysql的数 ...
- mysql锁相关讲解及其应用
一.mysql的锁类型 了解Mysql的表级锁 了解Mysql的行级锁 (1) 共享/排它锁(Shared and Exclusive Locks) 共享锁和排他锁是InnoDB引擎实现的标准行级别锁 ...
- Java学习1:图解Java内存分析详解(实例)
首先需要明白以下几点: 栈空间(stack),连续的存储空间,遵循后进先出的原则,用于存放局部变量. 堆空间(heap),不连续的空间,用于存放new出的对象,或者说是类的实例. 方法区(method ...
- bjdctf_2020_babyrop2
这道题是一道基本题,正因为它经典,所以需要重点记录一下. 这道题考察格式化字符串泄露canary,然后rop获得libc版本,之后拿到shell.拿到程序之后我们先检查一下保护... 开启了堆栈不可执 ...
- LuoguP2378 因式分解II 题解
Content 输入一个多项式 \(x^2+ax+b\)(不保证 \(a,b\neq0\)),请对这个多项式进行因式分解(形式为 \((x-x_1)(x-x_2)\),其中 \(x_1>x_2\ ...
- LuoguP3932 浮游大陆的68号岛 题解
Content 在一个无限长的数轴上有 \(n\) 个点.第 \(i\) 个点上面有 \(a_i\) 件物品,且第 \(i\) 个点到第 \(i+1\) 个点的距离为 \(b_i\). 定义从第 \( ...
- CF1106A Lunar New Year and Cross Counting 题解
Content 试求出在一个 \(n\times n\) 的地图 \(M\) 中,满足 \(1\leqslant i,j\leqslant n\) 且 \(M_{i,j}=M_{i+1,j+1}=M_ ...
- react 结合gitte 创建项目(详情步骤)
创建项目第一步 基本搭建 在创建之前,需要有一个git 仓库,我们要把项目搭建到git 中 目录介绍 cd 到某个盘 mkdir workspace 创建workspace文件夹 cd works ...
- 在react项目中使用require引入图片不生效
如果使用create-react-app和require导入图像,require返回一个ES模块而不是字符串.这是因为在file-loader中,esModule选项是默认启用的. 用以下方式之一导入 ...
- Centos7查看防火墙对应的开放端口以及进行端口操作
1.查看开放端口列表 [root@host bin]# firewall-cmd --list-ports 22/tcp 80/tcp 8888/tcp 39000-40000/tcp 12888/t ...