关于java:JEP解读与尝鲜系列4-Java-16-中对于-Project-Valhalla-的铺垫

33次阅读

共计 4376 个字符,预计需要花费 11 分钟才能阅读完成。

这是 JEP 解读与尝鲜系列的第 4 篇,之前的文章如下:

  • [JEP 解读与尝鲜系列 1 – Java Valhalla 与 Java Inline class]()
  • [JEP 解读与尝鲜系列 2 – JEP 142 缓存行填充简化]()
  • [JEP 解读与尝鲜系列 3 – Project Loom 应用虚线程进行同步网络 IO 不阻塞的底层原理]()

在系列之前的第一篇文章 – 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](https://docs.oracle.com/en/ja…),Java 11 中的定义是:像是 java.util.Optionaljava.time.LocalDateTime 这品种就是 Value-based Classes,这品种的实例:

  • 自身是不可变的,尽管外部的值援用指向的是一个可变对象
  • 实现了 equalshashCodetoString 办法,并且基于它蕴含的值实现,而不是基于他的 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/ja…),将原始类型的封装类,例如 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

一点趣事儿

Java 16 的 Record 还让我闹了个笑话,我认为这个是 Project Valhala 的 Inline Object 曾经实现了,还去 StackOverflow 问,这个 Record 为啥能有 wait() 办法,并且能够进行 synchronized 同步(因为如果是 Project Valhala 的 Inline Object 的话是没有一般类的对象头的,没法用一般类对象的办法实现同步),后果。。。。。最初还是 Goetz 大佬一眼就看出我是误会了

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

正文完
 0