这是JEP解读与尝鲜系列的第四篇文章。往期文章如下:【JEP解读与尝鲜系列1-JavaValhalla与JavaInline类】()【JEP解读与尝鲜系列2-JEP142缓存行填充简化】()【JEP解读与尝鲜系列3-ProjectLoom使用虚拟线程实现同步网络IO非阻塞底层原理]()第一系列之前在这篇文章-JEP解读与尝鲜系列1-JavaValhalla和JavaInline类中,我介绍了核心的JavaInlineClass在ProjectValhalla项目,其实就是Java中的值类型。目前Java中只有类对象,没有值类型对象。普通的类对象都有对象头,所以这种对象可以作为同步锁,利用它的wait()notify()等方法实现阻塞同步。同时需要在堆上分配这些对象,通过JVMGC进行内存回收。而这种对象的数组,只有数组本身是内存连续的,上面引用的对象不是:ProjectValhalla提出并设计实现了Java的值类型,去掉了对象头,里面只存值。这样减少了这种对象占用的空间,但是也使得这种对象无法使用对象的同步,也失去了对wait()notify()等方法的支持。同时,这种对象是希望直接分配在栈上的,不需要像普通对象一样分配在堆上,就像int等原始类型一样。同时,对于这种对象数组,期望数组中每个对象的内存在内存中是连续的:这样也节省了指针的存储空间。但是这些还在设计和实现中,不是最终的实现模型,但是可以看出趋势。为了最终实现ProjectValhalla,我们必须首先兼容JDK的一些元素。JDK中有哪些类与值类型有关首先首先想到的是Java的原始类型对应的封装类型,比如java.lang.Integer。原始类型可以也需要转化为Java值类型,但是要避免在项目中使用Integer对象的wait()、notify()、notifyAll()等方法,或者将其作为同步对象使用。于是想到了原子类,比如java.util.concurrent.atomic.AtomicInteger。实际上在Java9之后的JMM模型中实现了更细粒度的访问控制,例如:privateintlocked=0;privatestaticfinalVarHandleLOCKED;//操作锁定的句柄static{try{//初始化句柄LOCKED=MethodHandles.lookup().findVarHandle(currentclass.class,"locked",int.class);}catch(Exceptione){thrownewError(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);11官方文档中提出的Value-basedClasses,参考:[Java11Value-basedClasses](https://docs.oracle.com/en/ja...),Java11中的定义是:likeClasses像java.util.Optional和java.time.LocalDateTime是基于值的类。这个类的实例本身是不可变的,虽然内部值引用指向一个实现了equals、hashCode和toString方法的可变对象,并且是根据它包含的值来实现的,而不是根据它的身份(比如对象基地址)而不是基于其他对象的状态。不使用身份敏感操作,比如通过==比较两个实例是否相等,使用基于对象基地址的默认hashcode实现(例如调用System.identityHashCode(object)),用作同步的对象是只有通过equals比较的对象是相等的,不是==没有可访问的构造函数,而是通过工厂方法实例化的,这些方法不对返回实例的身份做任何保证,即我们不能向其传递参数的返回对象的地址工厂方法确保;两个相等的对象需要具有完全相同的行为。这种Value-basedClasses其实很符合Java值类型的特点。因此,从Java16开始,Value-basedClasses的定义被扩充,其使用受到告警的限制,暗示以后这些类型将不再由普通类实现,而是由Java的值类型实现瓦尔哈拉计划。JEP390:WarningsforValue-BasedClasses在Java16中,为了为ProjectValhalla的这个特性做铺垫,引入了一个JEP:JEP390:WarningsforValue-BasedClasses在最新的Value-basedClasses的定义中(参考:https://docs.oracle.com/en/ja...),原始类型的封装类,如java.lang.Integer,也包含在该类的定义中。并在此基础上,补充两点说明:1.不建议使用此类对象作为同步参数,如synchronize(obj),无法保证锁的拥有者是谁,是否独占。这个问题不是以后不能同步值类型造成的,而是很容易犯这个编程错误:Integeri=1;for(intj=0;j<10;j++){synchronized(i){我++;//下一次循环会变成另外一个对象,并没有像预期的那样真的加锁}}2.identity相关的操作以后可能会发生变化,所以不建议使用,例如:callSystem.identityHashCode(object)来获取基于堆内存地址处对象实现的哈希码,如果Value-basedClasses成为值类型,确定值类型后分配到栈上,目前该方法的机制会有问题.调用synchronize(obj)来同步对象。如果Value-basedClasses成为值类型,普通对象没有对象头,那么普通的锁扩展同步机制就不能用了。同时,无法使用权??重锁互斥锁,因为可能的值类型对象在堆上没有位置。使用现有机制实施。调用对象的wait()、notify()和notifyAll()。由于与上一篇文章相同的影响,这些方法调用可能会在以后的版本中出现异常。Java16之后,如果有这些用法,在编译阶段会有告警提醒:Integerinteger=1;synchronized(integer){}编译阶段会提示Attempttosynchronizeonaninstanceofavalue-basedclass,如果要关闭可以加上编译参数-Xlint:synchronization。如果想在运行阶段有这种使用的提示或者错误,可以加上如下启动参数:-XX:+UnlockDiagnosticVMOptions-XX:DiagnoseSyncOnValueBasedClasses=1:加上这个,程序遇到这种使用就会抛出FATALERROR,同时退出JVM。在com.github.hashjang.shenandoah.Main.main(Main.java:8)[0.152s][info][valuebasedclasses]上同步对象0x000000069aed7788ofklassjava.lang.Integer[0.152s][info][valuebasedclasses]-locked<0x000000069aed7788>(ajava.lang.Integer)同样,由于原始类型包装类已经属于Value-basedClass,因此不应使用其构造函数,而应使用valueOf(),以便为大家修改它在目前,只是将构造函数标记为DeprecateforRemoval:@Deprecated(since="9",forRemoval=true)publicInteger(intvalue){this.value=value;}会提示'Integer(int)'is弃用并标记为移除当前JDK中未来可能被值类型替代的Value-basedClass目前JDK中的Value-basedClasses都有jdk.internal.ValueBased注解,或者它们的实现接口,父类有该注解,包括:java.lang包:原始类型封装类,如java.lang.Integerjava.lang.Runtime.Version类操作系统进程句柄java.lang.ProcessHandle及其实现类java.lang.ProcessHandleImpljava.timepackage一些封装类java.utilpackage:Optional相关,例如:java.util.Optional,java.util.OptionalInt,java.util.OptionalLong,java.util.OptionalDouble底层实现的所有不可变集合和不可变元素,例如:Set.of返回java.util.ImmutableCollections.AbstractImmutableSet。有点意思。Java16的Record也把我逗笑了。我认为这是ProjectValhala的内联对象。去StackOverflow问了下,这个Record为什么可以有wait()方法,可以进行synchronized同步(因为如果是ProjectValhala的InlineObject,没有普通类的对象头,是无法与普通类对象的方法实现同步),结果。....最后Goetz老师一眼就看出我误会了:微信搜索“我的编程喵”关注公众号,每天刷一刷,轻松提升技能,赢取各种offer:
