平码五不中公式规律
  • / 70
  • 下载费用:30 金币  

变量句柄.pdf

关 键 ?#21097;?/dt>
变量 句柄
  专利查询网所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
摘要
申请专利号:

CN201580038162.7

申请日:

2015.07.13

公开号:

CN106663024A

公开日:

2017.05.10

当前法律状态:

实审

?#34892;?#24615;:

审中

法?#19978;?#24773;: 实质审查的生效IPC(主分类):G06F 9/455申请日:20150713|||公开
IPC分类号: G06F9/455 主分类号: G06F9/455
申请人: 甲骨文国?#20351;?#21496;
发明人: P·山德士; B·戈茨; J·R·罗斯
地址: 美国加利福尼亚
优?#28909;ǎ?/td> 2014.07.14 US 62/024,334; 2015.04.07 US 14/681,003; 2015.04.07 US 14/681,017
专利代理机构: 中国国际贸?#29366;?#36827;委?#34987;?#19987;利商标事务所 11038 代理人: 李晓芳
PDF完整版下载: PDF下载
法律状态
申请(专利)号:

CN201580038162.7

授权公告号:

|||

法律状态公告日:

2017.06.06|||2017.05.10

法律状态类型:

实质审查的生效|||公开

摘要

根据一种技术,虚拟机识别创建变量句柄实例的第一指令,该第一指令包括识别接收者的类型以及接收者保持的由该变量句柄实例被配置为提供对其的访?#23454;?#21464;量的声明信息。如果对变量的访问是允许的,则虚拟机创建变量句柄实例,该变量句柄实例包括被配置为对变量的存储器位置执行受约束操作的受约束函数。虚拟机识别指定对特定受约束的调用的第二指令,其中第二指令指定接收者或者被隐式绑定到接收者。虚拟机识别存储变量的实例的特定存储器位置并且相对于该特定存储器位置执行特定受约束函数。

权利要求书

1.一种方法,包括:
识别创建变量句柄实例的第一指令,所述第一指令包括识别接收者的类型以及由所述
接收者保持的所述变量句柄实例被配置为提供对其的访?#23454;?#21464;量的声明信息;
响应于所述第一指令,并?#19968;?#20110;所述声明信息,执行一个或多个检查以确定对所述变
量的访问是否是许可的;
响应于确定对变量的访问是许可的,创建变量句柄实例,所述变量句柄实例包括被配
置为对所述变量的存储器位置执行受约束操作的一个或多个受约束函数;
识别指定对所述变量句柄实例的所述一个或多个受约束函数中的特定受约束函数的
调用的第二指令,其中所述第二指令指示所述接收者;以及
识别存储所述变量的特定存储器位置并且使所述特定受约束函数相对于所述特定存
储器位置被执行。
2.如权利要求1所述的方法,其中所述接收者是类并且所述变量是由所述类保持的静
态字段、所述接收者是类实例并且变量是由所述类实例保持的字段、接收者是数组并且变
量是所述数组的元素、或者所述接收者是对表示所述变量的堆外或直接存储器位置的引
用。
3.如权利要求1-2中的任何一项权利要求所述的方法,其中所述第一指令调用查找函
数,所述查找函数使所述变量句柄实例存储所述变量在所述接收者内的偏移,以及将所述
变量句柄实例的所述一个或多个受约束函数绑定到识别所述接收者的类型和所述变量的
类型的一个或多个描述符。
4.如权利要求1-3中的任何一项权利要求所述的方法,其中所述方法由管理第一存储
器区域的虚拟机执行,并且所述变量的实例被存储在由所述虚拟机管理的第一存储器区域
外部的第二存储器区域中。
5.如权利要求1-4中的任何一项权利要求所述的方法,还包括:
识别指定对所述变量句柄实例的所述一个或多个受约束函数中的第二特定受约束函
数的第二调用的第三指令,所述第三指令指定共享所述接收者的类型的第二接收者;以及
识别其中所述变量由所述第二接收者保持的第二特定存储器位置,并且使得所述第二
特定受约束函数相对于所述第二特定存储器位置被执行。
6.如权利要求1-5中的任何一项权利要求所述的方法,其中所述方法由虚拟机执行,并
且虚拟机通过生成由所述虚拟机正在其上执行的硬件支持的机器代码指令来执行所述受
约束操作。
7.如权利要求1-6中的任何一项权利要求所述的方法,其中所述受约束操作执行以下
操作中的一个或多个:放宽的操作、?#36164;?#24615;操作、懒惰的操作、比较和设置操作或者获得和
设置操作。
8.如权利要求1-7中的任何一项权利要求所述的方法,其中第二指令包括用于所述特
定受约束函数的一个或多个?#38382;?#20197;及所述一个或多个?#38382;?#21253;括以下各项中的一个或多
个:所述变量的实例所位于的到所述接收者的实例的数组索引或者存储到所述变量的实例
的所述特定存储器位置的值。
9.如权利要求1-8中的任何一项权利要求所述的方法,还包括:执行检查,以确保由所
述第二指令指定的所述接收者的实例与由所述声明信息识别的所述接收者的类型匹配。
10.如权利要求1-9中的任何一项权利要求所述的方法,其中使所述特定受约束函数相
对于所述特定存储器位置被执行包括经由一条或多条硬件指令调用与?#25105;?#23384;储器位置交
互的函数。
11.如权利要求1-10中的任何一项权利要求所述的方法,其中所述变量句柄实例的所
述一个或多个受约束函数包括对应于一个或多个原子算术运算的函数。
12.如权利要求1-11中的任何一项权利要求所述的方法,其中所述变量句柄实例从扩
展抽象变量句柄类的变量句柄子类派生,所述抽象变量句柄类为所述一个或多个受约束函
数提供一个或多个接口,并且所述变量句柄子类相对于特定访问类型实现所述一个或多个
受约束函数。
13.如权利要求1-12中的任何一项权利要求所述的方法,还包括:响应于确定由所述第
二指令调用的所述特定受约束函数是签名多态的,基于表示对所述特定受约束函数的调用
的引用的描述符来自动启用预定义函数,其中所述预定义函数执行类型检查并且启用执行
所述特定受约束函数的变量句柄的函数。
14.一个或多个非暂态计算机可读介?#21097;?#25152;述一个或多个非暂态计算机可读介质存储
指令,当所述指令由一个或多个计算设备执行?#20445;?#20351;得如权利要求1-13中的任何一项权利
要求所述的步骤被执行。
15.一种系统,所述系统包括一个或多个计算设备,所述一个或多个计算设备包括至少
部分地由计算硬件实现的、被配置为实现如权利要求1-13中的任何一项权利要求所述的步
骤的组件。

说明书

变量句柄

技术领域

实施例一般涉及用于在编程语?#38405;?#25903;持和/或利用改进的存储器访?#23454;?#25216;术。

背景技术

在多处理器系?#25345;校?#22788;理器常常具有一个或多个存储器高速缓存层,该一个或多
个存储器高速缓存层通过加速对数据的访问和减少共享存储器总线上的业务二者来提高
性能。然而,虽然存储器高速缓存可以大大提高性能,但是它们也提出了新的挑战。例如,检
查同一存储器位置的两个处理器可能接收到不同的结果,因为一个处理器可能使用陈旧的
(stale)高速缓存值,然而另一个处理器可以从主存储器拉取更新的值。此外,许多编译器
和计算机架构重?#21019;?#30721;,以优化执行。例如,处理器可以重写或重新排序代码,以利用存储
在其高速缓存中的当前数据。然而,这些优化中的许多优化仅针对单个处理器/线程正在执
行程序的情况确保一致的程序语义。因此,在多处理器/多线程环?#25345;校?#37325;新排序可能导致
非预期的行为和不一致的程序状态。例如,假如直到指令的原始程序索引才?#35272;?#21464;量,则计
算机架构可能在最方便的时候提前执行加载/存储。然而,在多个线程或多个处理器的情况
下,提前执行由其它线程?#35272;?#30340;操作可能导致否则将不可能遇到的状态。

附图说明

在附图的图?#22411;?#36807;示例的方式而不是通过限制的方式示出本发明,在附图中相似
的附图标记指代相似的元件并且其中:

图1是示出根据各种实施例的、在其中可以实?#30452;?#25991;描述的某些技术的示例计算
架构的逻辑框图。

图2是示出根据实施例的示例类文件的框图。

图3是示出根据实施例的用于虚拟机运行?#34987;?#22659;的示例结构的框图。

图4是示出根据实施例的用于虚拟机栈上的帧的示例结构的框图。

图5示出了根据实施例的、以框图?#38382;?#30340;、用于使用VarHandle执行原子操作的示
例过程。

图6是示出适合于实?#30452;?#25991;所描述的方法和特征的计算机系统的一个实施例的框
图。

具体实施方式

在以下描述中,为?#31169;?#37322;的目的,阐述了许多具体?#38468;冢?#20197;便提供对本发明的透彻
理解。然而,将明显的是,可以在没有这些具体?#38468;?#30340;情况下实践本发明。在其它实例中,以
框图?#38382;?#31034;出了公知的结构和设备,以避免不必要地使本发明模糊。

本文根据以下提纲来描述实施例:

1.0总体概述

1.1受防护的操作

1.2原子操作

1.3暴露受约束的操作

2.0示例操作架构

2.1示例类文件结构

2.2示例虚拟机架构

2.3加载、链接和初始化

3.0VarHandle

3.1示例受约束的操作

3.2示例接口

3.3VarHandle过程流字?#38382;?#20363;

3.4VarHandle过程流数组示例

3.5优化

3.6放宽关于多态签名方法的返回类型

3.7通用多态签名

3.8VarHandle和MethodHandle之间的代码的合并

3.9存储器围栏

3.10VarHandle功能的扩展

4.0硬件概述

5.0扩展和可替代物

6.0附加公开

7.0第二附加公开

1.0.总体概述

本文描述了用于通过使用支?#25351;?#31181;不同存储器访问模式的“句柄”来提供对存储
器位置的安全?#36879;?#25928;的受约束访?#23454;?#25216;术。

1.1受防护的(fenced)操作

存储器模型定义了用于知道?#38382;?#30001;其它处理器对存储器的写入对当前处理器可
见以及由当前处理器的写入对其它处理器可见的条件。例如,一些处理器展现出强的存储
器模型,其中所有处理器对于任何给定的存储器位置都看到完全相同的值。其它处理器展
现出?#20808;?#30340;存储器模型,其中被称为存储器“屏障?#34987;頡?#22260;栏(fence)”的特殊指令冲刷
(flush)?#38236;?#22788;理器高速缓存或使?#38236;?#22788;理器高速缓存无效,以便看?#25509;?#20854;它处理器做出
的写入或者使处理器的写入对其它处理器可见。

上面提到的存储器屏障的效果是,使得在围栏之前的操作的存储器效果在围栏之
后的操作之前可见。因此,围栏可以被看作防止操作被跨围栏重新排序。在一些情况下,计
算架构提供对一般围栏的支持,该一般围栏防止在围栏之前的任何加载/存储操作与在围
栏之后的另一加载/存储操作的重新排序。然而,其它计算架构提供对细粒度围栏的支持,
该细粒度的围栏防止某些类型的操作的重新排序,诸如加载-加载围栏防止在围栏之前的
加载指令与围栏之后的其它加载指令的重新排序、加载-存储围栏防止在围栏之前的加载
操作与在围栏之后的存储操作的重新排序,等?#21462;?#32454;粒度的围栏通常比一般围栏需要更少
的开销,从而允许开发者通过使用提供所需功能的最便宜的围栏来优化其程序的执行。某
些类型的存储器访问操作利用围?#31119;?#20197;便提高程序一致性以及避免竞争条件(race
condition)。这些操作此后将被称为“受防护的操作?#34987;頡?#21463;防护的访问?#20445;?#20854;包括诸如放宽
的访问(relaxed access)、?#36164;?#24615;访问(volatile access)、懒惰的访问(lazy access)等
等之类的技术。

1.2原子操作

原子操作还可以被用来确保程序一致性。原子操作是像访?#23454;?#21333;个单元一样被执
行的操作。例如,获得和设置(get-and-set)原子操作替换变量的当前值并且“同时”返回变
量的旧值。在一些实例中,原子操作经由自旋锁(spinlock)和存储器围栏实现。然而,许多
计算机架构支持可以由底层硬件高效执行的本机(native)原子操作,而不需要在软件级经
由自旋锁和存储器围栏来实现。例如,处理器可以被配置为在相同的总线操作中读取存储
器位置并且写入存储器位置,从而防止其它处理器在原子操作的中间访问那个存储器位
置。如本文所使用的,“受约束的”操作/功能可以指代“受防护的”操作/功能和“原子”操作/
功能中的任一或二者,因为“受防护的”和“原子”操作/功能都对如何访问和/或操纵表示变
量的存储器位置应用约束。

1.3暴露受约束的操作

在一些情况下,如何以高效和方便的方式向这样的受约束的操作暴露接口可以是
重大的挑战。例如,受约束的操作可以经由对与?#25105;?#23384;储器位置交互的“不安全”库的访问
来执行。然而,对于许多语言和虚拟机开发者来说,允许编码者访问?#25105;?#23384;储器地址可能破
坏平台否则希望做到的安全保证,从而导致该语言/虚拟机被设计为最小化或彻底防止其
发生的非期望的行为,诸如分?#26410;?#35823;或者更糟。例如,“不安全”库可以是针对特定处理器类
型优化的本机库,但是可以不检查“更高级”语义,诸如确保对数组位置的写入在界限内。由
于在使用“不安全”库时出现的复?#26377;裕?#35821;言和虚拟机开发者常常将防止或最小化非预期行
为的可能性的责任放在编码者?#31181;小?br />

另一个潜在的解决方案是将对“不安全”库的访问限制为已知具有以安全?#36879;?#36131;
的方式使用“不安全”库的专门知识的受信任的开发者。然而,将对受约束的操作的访问限
制为开发者的小子集阻碍了其他开发者创建健壮?#36879;?#25928;的软件产品的能力。

1.4VARHANDLE

在实施例中,VarHandle(“变量句柄”的缩写)是对存储器的可执行引用,诸如对由
(一个或多个)(受管理或不受管理的)存储器位置定义的存储器、(一个或多个)类的(一个
或多个)对象或(一个或多个)实例化、(一个或多个)对象的(一个或多个)字段、(一个或多
个)类的(一个或多个)静态字段、或者(一个或多个)数组的(一个或多个)元素的可执行引
用。存储在被引用的存储器中的值可以被称为(一个或多个)变量,并且VarHandle提供开发
者可以通过其访问要对(一个或多个)变量执行的受约束的操作的安全接口。在实施例中,
VarHandle经由保持变量的接收者(诸如保持字段的对象实例或保持元素的数组)来访问变
量。在?#22270;?#21035;,经由对与底层计算机硬件架构通信并且访问?#25105;?#23384;储器位置的“不安全”方
法的调用来执行受约束的操作。在一些实施例中,VarHandle被实现为类,并且该类的实例
化被称为VarHandle实例或对象。

例如,在Java的背景下,受约束的操作可以通过对“sun.misc.Unsafe”的调用来执
行,该“sun.misc.Unsafe”实现用于对表示各种变量(诸如对象、整型(int)、浮点型
(float)、长整型(long)、双精度型(double)等)的?#25105;?#23384;储器位置执行受约束的操作的方
法。然而,在许多情况下,将这些“不安全”方法暴露给软件开发者造成存储器位置可能以虚
拟机不能预期的方式被操纵的风险。这可能导致虚拟机的设计者努力减轻或阻止的运行时
错误和/或?#35272;#?#35832;如分?#26410;?#35823;。在实施例中,VarHandle提供了一种在方便和易于使用的接
口中向开发者暴露这些?#22270;?#30340;受约束的操作的安全方式。此外,在一些实施例中,虚拟机被
配置为优化访问和安全检查的方式,以实现与暴露到“不安全”方法的直接接口?#36127;?#30456;同的
运行时性能,但是没有直接利用“不安全”方法的缺点。

因此,VarHandle是将大大有益于软件社区的、以安全、方便?#36879;?#25928;的方式向开发
者暴露受约束的操作的机制。例如,“无阻塞算法”的领域是涉及使用受约束的访问作为基
本并发原语的高性能并发数据结构的研究领域。因此,提供其中?#23454;?#29087;练的开发者可以容
易且安全地实现这样的算法的环境将对该领域有显著益处。

2.0示例操作架构

图1示出了本文描述的技术可以在其中实践的、包括运行?#34987;?#22659;112的示例计算架
构100。本文描述的技术常常使用来自Java编程语言、Java虚拟机(“JVM”)和Java运行?#34987;?br />境的术语和定义。然而,设想的是,所描述的技术可以与任何编程语言、虚拟机架构或运行
?#34987;?#22659;结合使用。因此,例如,诸如“方法”之类的以Java术语描述的术语可以与其它术语
(诸如“函数”)互换。此外,术语“方法?#34987;?#19982;术语“类方法?#34987;頡?#23545;象方法”同义。方法是通过名
称指代并且可以在使得方法的代码被执行的程序中的各个点处被调用(启用)的代码集合。

如图1中所示,运行?#34987;?#22659;112包括虚拟机104。虚拟机104包括各种组件,诸如存储
器管理器105(其可以包括垃圾收集器)、用于检查虚拟机104代码的?#34892;?#24615;的类文件验证器
106、用于定位和构建类的存储器中(in-memory)表示的类加载器107、以及用于执行虚拟机
104代码的解释器108(其可以包括即时“JIT”编译器)。在一些实施例中,解释器108实?#32440;?br />释器和JIT编译器二者的方面。

在实施例中,计算架构100包括?#21019;?#30721;文件101,?#21019;?#30721;文件101包含以特定编程语
言(诸如Java、C、C++、C#、Ruby、Perl等)编写的代码。因此,?#21019;?#30721;文件101遵守用于相关联
语言的语法和/或语义规则的特定集合。例如,用Java编写的代码遵守Java语言规范。然而,
由于规?#31471;?#26102;间被更新和修订,因此?#21019;?#30721;文件101可以与指示?#21019;?#30721;文件101所遵守的规
范的修订的版本号相关联。用来编写?#21019;?#30721;文件101的确切的编程语言一般不是关键的。

在各种实施例中,编译器102将根据依照程序员的方便的规范编写的?#21019;?#30721;转换
为可由特定机器环?#25345;?#25509;执行的机器代码或目标代码,或者可由能够在各种特定机器环境
之上运行的虚拟机104执行的中间表示,诸如?#32440;?#30721;。?#32440;?#30721;可由虚拟机104以比?#21019;?#30721;更
直接?#36879;?#25928;的方式执行。将?#21019;?#30721;转换为?#32440;?#30721;包括将来自语言的?#21019;?#30721;功能?#25104;?#21040;利用
诸如数据结构之类的底层资源的虚拟机功能。通常,由程序员经由?#21019;?#30721;简单地呈现的功
能被转换为更直接地?#25104;?#20026;在由虚拟机104执行期间将引起的机器操作的较复杂的步骤。

在一些实施例中,解释器108实?#32440;?#37322;器和JIT编译器二者的方面。例如,Oracle的
HotSpot解释来自“?#32440;?#30721;”的代码,而且定位被频繁执行(“热”)的?#32440;?#30721;的部分并且将那
些部分编译成适?#31995;?#23618;计算机硬件的处理器的高效机器代码。运行?#34987;?#22659;120可以在诸如
操作系统110之类的较?#22270;度?#20214;之上运行,在一些实施例中,该较?#22270;度?#20214;通过一个或多个
应用编程接口(API)而被访问。虚拟机104、API 109和操作系统110的组合被称为执行平台
111。

为了提供清楚的示例,?#21019;?#30721;文件101被示为要由执行平台111执行的程序的“最
高级”表示。然而,虽然计算架构100将?#21019;?#30721;文件101描绘为“最高级”程序表示,但是在其
它实施例中,?#21019;?#30721;文件101可以是经由将不同语言的代码文件处理?#31245;创?#30721;文件101的语
言的“较高级”编译器接收的中间表示。为了说明清楚的示例,以下公开假设?#21019;?#30721;文件101
遵守基于类的面向对象的编程语言。然而,这不是利用本文所描述的特征的要求。

在实施例中,编译器102接收?#21019;?#30721;文件101作为输入,并且将?#21019;?#30721;文件101转换
为具有虚拟机104所期望的格式的类文件103。例如,在JVM的背景下,Java虚拟机规范的第4
章定义了期望类文件103遵守的特定类文件格式。在一些实施例中,类文件103包含已经从
?#21019;?#30721;文件101转换而来的?#32440;?#30721;。然而,在其它实施例中,类文件103还可以包含其它结
构,诸如识别与各种结构(类、字段、方法等)相关的常量值和/或元数据的表。

以下?#33268;?#23558;假设类文件103中的每个类文件表示在?#21019;?#30721;文件101中定义的(或由
编译器102/虚拟机104动态生成的)相应的“类”。然而,上面提到的假设不是严格的要求并
且将取决于虚拟机104的实现。因此,?#36824;?#31867;文件103的确切格式如何,本文描述的技术仍然
可以被执行。在一些实施例中,类文件103被划分为一个或多个“库?#34987;頡?#21253;?#20445;?#35813;一个或多个
“库?#34987;頡?#21253;”中的每个包括提供相关功能的类的集合。例如,库可以包含实现输入/输出(I/
O)操作、数学工具、密码技术、图形实用程序等的一个或多个类文件。此外,一些类(或那些
类内的字段/方法)可以包括访问限制,该访问限制将它们的使用限制在特定类/库/包内或
者限制为具有?#23454;?#35768;可(permission)的类。

2.1示例类文件结构

图2示出了根据实施例的、以框图?#38382;?#30340;用于类文件200的示例结构。为了提供清
楚的示例,本公开的剩余部分假设计算架构100的类文件103遵守本节中描述的示例类文件
200的结构。然而,在实际环?#25345;校?#31867;文件200的结构将?#35272;?#20110;虚拟机104的实现。此外,本文
?#33268;?#30340;一个或多个特征可以修改类文件200的结构,以便例如添加附加的结构类型。因此,
类文件200的确切结构对于本文所描述的技术不是关键的。为?#31169;?.1的目的,“类?#34987;頡?#35813;
类”是指由类文件200表示的类。

在图2中,类文件200包括常量表201、字段结构208、类元数据204和方法结构209。

在实施例中,常量表201是除其他功能之外还充当类的符号表的数据结构。例如,
常量表201可以存储与在?#21019;?#30721;文件101中使用的各种标识符(诸如类型、?#27573;А?#20869;容和/或
位置)相关的数据。常量表201具有用于由编译器102从?#21019;?#30721;文件101导出的?#21040;?#26500;202(表
示类型整型、长整型、双精度型、浮点型、?#32440;?#22411;(byte)、?#22336;?#20018;型(string)等的常量值)、类
信息结构203、名称和类型信息结构205、字?#25105;?#29992;结构206以及方法引用结构207的条目。在
实施例中,常量表201被实现为将索引i?#25104;?#21040;结构j的数组。然而,常量表201的确切实现不
是关键的。

在一些实施例中,常量表201的条目包括索引其它常量表201条目的结构。例如,用
于表示?#22336;?#20018;的?#21040;?#26500;202中的一个?#21040;?#26500;的条目可以保持将其“类型”识别为?#22336;?#20018;的标
签,以及对存储表示该?#22336;?#20018;的ASCII?#22336;?#30340;?#22336;?#22411;(char)、?#32440;?#22411;或整型值的常量表201
的一个或多个其它?#21040;?#26500;202的索引。

在实施例中,常量表201的字?#25105;?#29992;结构206保持对常量表201中的类信息结构203
中表示定义字段的类的一个类信息结构的索引以及对常量表201中的名称和类型信息结构
205中提供字段的名称和描述符的一个名称和类型信息结构的索引。常量表201的方法引用
结构207保持对常量表201中的类信息结构203中表示定义方法的类的一个类信息结构的索
引以及对常量表201中的名称和类型信息结构205中提供用于方法的名称和描述符的一个
名称和类型信息结构的索引。类信息结构203保持对常量表201中的?#21040;?#26500;202中保持相关
联的类的名称的一个?#21040;?#26500;的索引。名称和类型信息结构205保持对常量表201中的?#21040;?#26500;
202中存储字段/方法的名称的一个?#21040;?#26500;的索引以及对常量表201中的?#21040;?#26500;202中存储
描述符的一个?#21040;?#26500;的索引。

在实施例中,类元数据204包括用于类的元数据,诸如(一个或多个)版本号、常量
池中的条目数、字?#38382;?#26041;法数、访问标志(类是否是公有的、?#25509;?#30340;、最终的、抽象的,等
等)、对常量表201的类信息结构203中识别该类的一个类信息结构的索引、对常量表201的
类信息结构203中识别超类(如果有的话)的一个类信息结构的索引,等?#21462;?br />

在实施例中,字段结构208表示识别类的各个字段的一组结构。字段结构208为类
的每个字段存储用于该字段的访问器标志(字?#38382;?#21542;是静态的、公有的、?#25509;?#30340;、最终的,等
等)、对常量表201中的?#21040;?#26500;202中保持字段的名称的一个?#21040;?#26500;的索引、以及对常量表
201中的?#21040;?#26500;202中保持字段的描述符的一个?#21040;?#26500;的索引。

在实施例中,方法结构209表示识别类的各种方法的一组结构。方法结构209为类
的每个方法存储用于该方法的访问器标志(例如,该方法是否是静态的、公共的、?#25509;?#30340;、同
步的,等等)、对常量表201中的?#21040;?#26500;202中保持方法的名称的一个?#21040;?#26500;的索引、对常量
表201中的?#21040;?#26500;202中保持方法的描述符的一个?#21040;?#26500;的索引、以及与如?#21019;?#30721;文件101
中定义的方法的主体对应的虚拟机104指令。

在实施例中,描述符表示字?#20301;?#26041;法的类型。例如,描述符可以被实现为遵守特定
语法的?#22336;?#20018;。虽然确切的语法并?#36824;?#38190;,但是下文描述几个示例。

在描述符表示字段的类型的示例中,描述符识别由该字段保持的数据的类型。在
实施例中,字段可以保持基本类型、对象或数组。当字段保持基本类型?#20445;?#25551;述符是识别该
基本类型的?#22336;?#20018;(例如,“B?#20445;絙yte(?#32440;?#22411;)、“C?#20445;絚har(?#22336;?#22411;)、“D?#20445;絛ouble(双精度
型)、“F?#20445;絝loat(浮点型)、“I?#20445;絠nt(整型)、“J?#20445;絣ong int(长整型)等)。当字段保持对象
?#20445;?#25551;述符是识别该对象的类名称的?#22336;?#20018;(例如,“L ClassName”)。在这种情况下,“L”指
示引用,因此“L ClassName”表示对类ClassName的对象的引用。当字?#38382;?#25968;组?#20445;?#25551;述符识
别由该数组保持的类型。例如,“[B”指示?#32440;?#22411;的数组,其中“[”指示数组,而“B”指示该数
组保持?#32440;?#22411;的基本类型。然而,由于数组可以嵌套,因此数组的描述符还可以指示嵌套。
例如,“[[L ClassName”指示数组,在该数组中每个索引保持保持类ClassName的对象的数
组。在一些实施例中,ClassName是完全限定的并且包括类的简单名称以及类的路径名称。
例如,ClassName可以指示文件存储在托管类文件200的包、库或文件系?#25345;?#30340;?#26410;Α?br />

在方法的情况下,描述符识别方法的?#38382;?#21644;方法的返回类型。例如,方法描述符可
以遵循一般?#38382;健?{ParameterDescriptor})ReturnDescriptor?#20445;?#20854;中
{ParameterDescriptor}是表示?#38382;?#30340;字段描述符的列表,而ReturnDescriptor是识别返
回类型的字段描述符。例如,?#22336;?#20018;“V”可以被用于表示void(空)返回类型。因此,在?#21019;?#30721;
文件101中被定义为“Object m(int I,double d,Thread t){…}”的方法匹配描述符“(I D
L Thread)L Object”。

在实施例中,方法结构209中保持的虚拟机104指令包括引用常量表201的条目的
操作。

使用Java作为示例,考虑以下类


在上面的示例中,Java方法add12and13在类A中定义、不带?#38382;?#24182;且返回整数。方
法add12and13的主体调用采用常量整数值12和13作为?#38382;?#30340;类B的静态方法addTwo,并且
返回结果。因此,在常量表201中,编译器102除其它条目之外还包括对应于对方法B.addTwo
的调用的方法引用结构。在Java中,对方法的调用向下编译为JVM的?#32440;?#30721;中的invoke命令
(在该情况中为invokestatic,因为addTwo是类B的静态方法)。向invoke命令提供对应于识
别定义addTwo的类“B”、addTwo的名称“addTwo”以及addTwo的描述符“(I I)I”的方法引用
结构的对常量表201的索引。例如,假设上面提到的方法引用被存储在索引4处,则?#32440;?#30721;指
令可以看起来是“invokestatic#4”。

由于常量表201用携带识别信息的结构以符号?#38382;?#25351;代类、方法和字段,而不是用
对存储器位置的直接引用来指代类、方法和字段,因此常量表201的条目被称为“符号引
用”。符号引用被用于类文件103的一个原因是因为在一些实施例中编译器102不知道类一
旦被加载到运行?#34987;?#22659;112中将怎样被存储以及将被存储在?#26410;Α?#22914;将在节2.3中描述的,
在所引用的类(和相关联的结构)已经被加载到运行?#34987;肪持?#24182;且已经被分配?#21496;?#20307;的存
储器位置之后,最终,符号引用的运行时表示由虚拟机104解析为实?#23454;?#23384;储器地址。

2.2示例虚拟机架构

图3示出了根据实施例的以框图?#38382;?#30340;示例虚拟机存储器布局300。为了提供清楚
的示例,剩余的?#33268;?#23558;假设虚拟机104遵守图3中所描绘的虚拟机存储器布局300。此外,虽
然虚拟机存储器布局300的组件可以被称为存储器“区域?#20445;?#20294;是不要求存储器区域是相邻
的。

在图3示出的示例中,虚拟机存储器布局300被划分为共享区域301和线程区域
307。

共享区域301表示存储器中存储在虚拟机104上执行的各种线程之间共享的结构
的区域。共享区域301包括堆302和按类的(per-class)区域303。在实施例中,堆302表示从
其分配用于类实例和数组的存储器的运行时数据区域。在实施例中,按类的区域303表示存
储与单独的类有关的数据的存储器区域。在实施例中,对于每个加载的类,按类的区域303
包括表示来自类的常量表201的数据的运行时常量池304、字段和方法数据306(例如,为了
保持类的静态字段)以及表示用于类的方法的虚拟机104指令的方法代码305。

线程区域307表示其中存储特定于单独的线程的结构的存储器区域。在图3中,线
程区域307包括表示由不同线程利用的每线程结构的线程结构308和线程结构311。为了提
供清楚的示例,图3中所描绘的线程区域307假设两个线程正在虚拟机104上执行。然而,在
实际环?#25345;校?#34394;拟机104可以执行任何?#25105;?#25968;量的线程,其中相应地缩放线程结构的数量。

在实施例中,线程结构308包括程序计数器309和虚拟机栈310。类似地,线程结构
311包括程序计数器312和虚拟机栈313。在实施例中,程序计数器309和程序计数器312存储
由它们各自的线程执行的虚拟机指令的当前地址。因此,当线程逐句通过(step through)
指令?#20445;?#31243;序计数器被更新以维护对当前指令的索引。在实施例中,虚拟机栈310和虚拟机
栈313各自存储保持局部变量和部分结果的、用于它们各自的线程的帧,并?#19968;?#34987;用于方法
启用和返回。

在实施例中,帧是被用?#21019;?#20648;数据和部分结果、返回用于方法的值以及执行动态
链接的数据结构。?#30475;?#21551;用方法时都创建新的帧。当使得帧被生成的方法完成?#20445;?#24103;被销
毁。因此,当线程执行方法启用(invocation)?#20445;?#34394;拟机104生成新帧并将该帧推送?#25509;?#35813;
线程相关联的虚拟机栈上。当方法启用完成?#20445;?#34394;拟机104将方法启用的结果传递回前一
帧,并且使当前帧出栈。在实施例中,对于给定的线程,在任?#38382;?#38388;点处有一个帧是活动的。
这个活动帧被称为当前帧,使得当前帧被生成的方法被称为当前方法,并?#19994;?#21069;方法所属
的类被称为当前类。

图4示出了根据实施例的以框图?#38382;?#30340;示例帧400。为了提供清楚的示例,剩余的
?#33268;?#23558;假设虚拟机栈310和虚拟机栈313的帧遵守帧400的结构。

在实施例中,帧400包括局部变量401、操作数栈402和运行时常量池引用表403。

在实施例中,局部变量401被表示为各自保?#31181;?#22914;?#32423;?#22411;(Boolean)、?#32440;?#22411;、?#22336;?br />型、短整型、整型、浮点型、引用型等之类的值的变量的数组。此外,诸如长整型或双精度型
之类的一些值类型可以由数组中的多于一个条目表示。局部变量401被用来在方法启用时
传递?#38382;?#20197;及存储部分结果。例如,当响应于启用方法而生成帧400?#20445;问?#21487;以被存储在
局部变量401内预定义的位置中,诸如对应于启用中的第一个至第N个?#38382;?#30340;索引1-N。

在实施例中,当帧400由虚拟机104创建?#20445;?#25805;作数栈402默认是空的。然后,虚拟机
104提供来自当前方法的方法代码305的指令,以将来自局部变量501的常量或值加载到操
作数栈502上。其它指令从操作数栈402取出操作数、对它们进行操作并且将结果推回到操
作数栈402上。此外,操作数栈402被用来准备要被传递到方法的?#38382;?#20197;及接收方法结果。例
如,在发出对方法的启用之前,被启用的方法的?#38382;?#21487;以被推送到操作数栈402上。然后,虚
拟机104生成用于方法启用的新帧,其中前一帧的操作数栈402上的操作数出栈并且被加载
到新帧的局部变量401中。当被启用的方法终止?#20445;?#26032;帧从虚拟机栈出栈并且返回值被推送
到前一帧的操作数栈402上。

在实施例中,运行时常量池引用表403包含对当前类的运行时常量池304的引用。
运行时常量池引用表403被用来支持解析(resolution)。解析是这样的过程?#21644;?#36807;该过程,
常量池304中的符号引用被翻译成具体的存储器地址,从而按照需要加载类以解析尚未定
义的符号并且将变量访问翻译成与这些变量的运行时位置相关联的存储结构中的?#23454;?#20559;
移。

2.3加载、链接和初始化

在实施例中,虚拟机104动态地加载、链接和初始化类。加载是寻找具有特定名称
的类并?#20197;?#36816;行?#34987;?#22659;112的存储器内创建来自该类的相关联的类文件200的表示的过程。
例如,在虚拟机存储器布局300的按类的区域303内创建用于类的运行时常量池304、方法代
码305以及字段和方法数据306。链接是取出类的存储器中表示并且将其与虚拟机104的运
行时状态组合以使得类的方法可以被执行的过程。初始化是执行类构造函数
(constructor)以设置类的字段和方法数据306的起始状态和/或为被初始化的类在堆302
上创建类实例的过程。

以下是可以由虚拟机104实现的加载、链接和初始化技术的示例。然而,在许多实
施例中,步骤可以是交错,以使?#36152;?#22987;类被加载,然后在链接期间第二个类被加载以解析在
第一个类中发现的符号引用,这?#20540;?#33268;第三个类被加载,等?#21462;?#22240;此,通过加载、链接和初始
化的阶段的进程可以根据类而不同。此外,一些实施例可以?#26144;?“懒惰地”执行)加载、链接
和初始化过程的一个或多个功能,直到类被实际需要。例如,方法引用的解析可以被?#26144;?#30452;
到启用被引用的方法的虚拟机104指令被执行。因此,对于每个类?#38382;?#25191;行步骤的确切时机
在实施方式之间可以差别很大。

为了开?#25216;?#36733;过程,虚拟机104通过启用加载初始类的类加载器107来启动。用于
指定初始类的技术将根据实施例而不同。例如,一种技术可以使虚拟机104在启动时接受指
定初始类的命令行变元(argument)。

为了加载类,类加载器107解析与该类对应的类文件200,并且确定类文件200是否
为?#38382;?#33391;好的(满足虚拟机104的语法期待)。如果不是,则类加载器107生成错误。例如,在
Java中,可能以异常的?#38382;?#29983;成错误,该异常被抛给异常处理器以供处理。否则,类加载器
107通过在按类的区域303内分配用于类的运行时常量池304、方法代码305以及字段和方法
数据306来生成类的存储器中表示。

在一些实施例中,当类加载器107加载类?#20445;?#31867;加载器107还递归地加载被加载类
的超类。例如,虚拟机104可以确保在继续进行对特定类的加载、链接和初始化过程之前该
特定类的超类被加载、链接和/或初始化。

在链接期间,虚拟机104验证类、准备类并且执行在类的运行时常量池304中定义
的符号引用的解析。

为?#25628;?#35777;类,虚拟机104检查类的存储器中表示在结构上是否是正确的。例如,虚
拟机104可以检查除了泛型(generic)类对象(Object)之外的每个类具有超类、检查最终
(final)类没有子类以及最终方法没有被重写(override)、检查常量池条目是否彼此一致,
检查当前类是否具有对常量池304中引用的类/字段/结构的正确访问许可、检查方法的虚
拟机104代码不会引起非预期行为(例如,确保跳转指令不会使虚拟机104超出方法的末
尾),等?#21462;?#22312;验证期间执行的确切检查取决于虚拟机104的实现。在一些情况下,验证可以
导致附加的类被加载,但是在继续进行之前不一定要求那些类也被链接。例如,假设类A包
含对类B的静态字段的引用。在验证期间,虚拟机104可以检查类B,以确保所引用的静态字
段实际存在,这可能导致类B的加载,但不一定导致类B的链接或初始化。然而,在一些实施
例中,某些验证检查可以被?#26144;?#30452;到较晚的阶段,诸如在符号引用的解析期间被检查。例
如,一些实施例可以推迟检查对于符号引用的访问许可直到这些引用被解析。

为了准备类,虚拟机104将位于该类的字段和方法数据306内的静态字段初始化为
默认值。在一些情况下,将静态字段设置为默认值可以与运行类的构造函数不同。例如,验
证过程可以在初始化期间将静态字段清零或将静态字段设置为构造函数期望这些字段具
有的值。

在解析期间,虚拟机104从包括在类的运行时常量池304中的符号引用动态地确定
具体的存储器地址。为?#31169;?#26512;符号引用,虚拟机104利用类加载器107加载符号引用中识别
出的类(如果还未加载的话)。一旦被加载,则虚拟机104知?#28010;?#24341;用的类和它的字段/方法
的按类的区域303内的存储器位置。然后,虚拟机104用对所引用的类、字?#20301;?#26041;法的具体存
储器位置的引用来替换符号引用。在实施例中,虚拟机104高速缓存解析,以便在当虚拟机
104处理另一个类时遇到相同的类/名称/描述符的情况下重用。例如,在一些情况下,类A和
类B可以启用类C的同一方法。因此,当对类A执行解析?#20445;?#35813;结果可以被高速缓存并?#20197;?#31867;B
中的相同符号引用的解析期间被重用,以减少开销。

在一些实施例中,在链接期间解析符号引用的步骤是可选的。例如,实施例可以以
“懒惰的”方式执行符号解析,从而?#26144;?#35299;析的步骤直到需要所引用的类/方法/字段的虚拟
机104指令被执行。

在初始化期间,虚拟机104执行类的构造函数以设置该类的起始状态。例如,初始
化可以初始化该类的字段和方法数据306,以及在由构造函数创建的堆302上生成/初始化
任何类实例。例如,用于类的类文件200可以指定特定方法是用于设置起始状态的构造函
数。因此,在初始化期间,虚拟机104执行该构造函数的指令。

在一些实施例中,虚拟机104通过最初检查字段/方法在所引用的类中是否被定义
来执行对字段和方法引用的解析。否则,虚拟机104针对所引用的方法/字段递归搜索所引
用的类的超类直到字段/方法被定位或到达最高级的超类,在到达最高级的超类的情况下
生成错误。

3.0VARHANDLE

在实施例中,VarHandle是对存储器的可执行引用,诸如对由(一个或多个)(受管
理或不受管理的)存储器位置定义的存储器、(一个或多个)类的(一个或多个)对象或(一个
或多个)实例化、(一个或多个)对象的(一个或多个)字段、(一个或多个)类的(一个或多个)
静态字?#20301;?#32773;(一个或多个)数组的(一个或多个)元素的可执行引用。在一些实施例中,
VarHandle被实现为可以被实例化以创建VarHandle实例/对象的类。在实施例中,
VarHandle经由保持变量的接收者(诸如保持字段的对象实例或保持元素的数组)来访问变
量。在?#22270;?#21035;,经由对与底层计算机硬件架构通信并且访问?#25105;?#23384;储器位置的?#22270;?#20869;部函
数(intrinsic)的调用来执行受约束的操作。例如,在Java的背景下,可以通过对
“sun.misc.Unsafe”的调用来执行受约束的操作,该“sun.misc.Unsafe”实现用于对表示各
种变量(诸如对象、整型、浮点型、长整型、双精度型等)的?#25105;?#23384;储器位置执行受约束的操
作的方法。然而,在许多情况下,向软件开发者暴露这些“不安全”方法造成存储器位置可能
以虚拟机104不能预期的方式被操纵的风险。这可能导致虚拟机104的设计者努力减轻或阻
止的运行时错误和/或?#35272;#?#35832;如分?#26410;?#35823;。

一种先前的解决方案是将对“不安全”方法的访问限制为已知具有以安全?#36879;?#36131;
的方式使用“不安全”库的专门知识的受信任的开发者。然而,将对受约束的操作的访问限
制为开发者的小子集阻碍了其他开发者创建健壮?#36879;?#25928;的软件产品的能力。在实施例中,
VarHandle提供了一种在方便和易于使用的接口中向开发者暴露这些?#22270;?#21463;约束的操作的
安全方式。此外,在一些实施例中,虚拟机104被配置为优化访问和安全检查的方式,以实现
与暴露到“不安全”方法的直接接口?#36127;?#30456;同的运行时性能,但是没有允许对“不安全”方法
的直接访?#23454;?#22266;有缺点。

3.1示例受约束的操作

本文描述的技术适用于?#36127;?#20219;何类型的受约束的操作。然而,为了提供清楚的示
例,参考VarHandle描述的操作将包括放宽的获得(relaxed-get)和放宽的设置(relaxed-
set)、?#36164;?#24615;获得和?#36164;?#24615;设置,获取获得(acquire-get)和释放设置(release-set)(懒惰
的获得/设置),以及比较和设置(compare-and-set)和获得和设置(get-and-set)。然而,不
同的实施例可以实现受约束操作的不同集合。例如,其它实施例还可以支持原子的加、减或
其它原子算术运算。

在实施例中,放宽的获得和放宽的设置执行放宽的受防护操作。在放宽的受防护
操作中,仅在特定线程的执行内保证排序。因此,如果一个线程执行存储,则不能保证另一
个线程将以相同的顺序看到存储。例如,如果线程A执行对变量a的存储1,然后执行对变量b
的存储2,则有可能对变量b的存储在对变量a的存储之前对另一线程B变得可见。

在实施例中,?#36164;?#24615;获得和?#36164;?#24615;设置执行?#36164;允?#38450;护操作。在?#36164;允?#38450;护
操作中,在?#36164;?#24615;存储和?#36164;约?#36733;之间实施顺序排序。因此,?#36164;允?#38450;护操作用直接对
主存储器执行的读取和写入来绕过?#38236;?#32447;程高速缓存。

在实施例中,获取获得和释放设置执行懒惰的受防护操作。在懒惰的受防护操作
中,在释放设置和获取获得之间执?#22411;?#27493;,以确保在执行期间的特定点处发生顺序
(happens-before)关系在线程之间可以继续保留。例如,如果线程A使用释放设置来执行对
变量a的存储1,然后使用释放设置来执行对变量b的存储2,则变量a和变量b的存储之间的
发生顺序关系相对于后续的获取获得被维持。因此,如果线程B在对b的存储变得可见之后
使用获取获?#32654;?#35835;取变量b,则将保证对a的存储会在线程B的上下文中预先发生。因此,一
旦对b的存储作为获取获得的结果而可见,则对a的存储也对线程B可见。

在实施例中,比较和设置是将存储器位置的内容与给定?#21040;?#34892;比较并且仅当比较
为真(true)时将内容修改为新值的原子操作。在实施例中,获得和设置是将值写入存储器
位置并且返回存储器位置的旧值的原子操作。

在一些实施例中,VarHandle还支持对变量的“正常?#34987;頡?#26080;约束”访问,该“正常?#34987;?br />“无约束”访问不提供关于对存储器中的变量的存储/加载的原?#26377;?#25110;重新排序的保证。因
此,如本文所描述的实施例适用于宽?#27573;?#30340;排序和原?#26377;?#32422;束,包括与“正常”存储器访问
相关联的“空(null)”约束。因此,在一些实施例中,“正常?#34987;頡?#26080;约束”访问模式是使用
VarHandle可用的若干选项中的一个选项,其中每个选项对存储器访问施加不同级别的约
束。

3.2示例接口

如上面所提到的,VarHandle引用保持变量的“接收者?#20445;?#35832;如保持字段的对象实例
或保持元素的数组。“接收者”的类型和由“接收者”保持的“变量”的类型是特定于实现的。
本文所描述的技术适用于?#36127;?#20219;何类型的“接收者”和“变量”。然而,以下表示可以经由
VarHandle访?#23454;摹?#25509;收者”和“变量”的示例。

在实施例中,VarHandle保持对以下的一个或多个的引用:(1)静态字段,其中接收
者是保存静态字段的类,(2)实例字段,其中接收者是保持字段的类的实例,或(3)数组元
素,其中接收者是在数组中的定义的索引处保?#25351;?#20803;素的数组。因此,每个接收者与特定类
型的变量访问相关联,诸如(1)参考静态访问、(2)参考实例访问,及(3)参考数组访问。

然而,在一些实施例中,VarHandle表示参考虚拟机104外部的堆外(off-heap)或
不受管理的存储器位置的接收者。例如,可以替代地使用基于库的接收者类型,而不是基于
语言的接收者类型,诸如保持字段的类/保持元素的数组。在这种情况下,接收者类型可以
在具有作为接收者接受该类的实例的对应VarHandle实现的库中被定义。例如,一个这样的
表示可以是保持到堆外/直接/不受管理的存储器的基地址(base address)以及界限的接
收者类型,其中存储器访问被约束为[基地址,基地址+界限]。然后,VarHandle将存储与该
接收者类型相关的数据并且相应地使用基地址/界限来访问存储器。

在实施例中,由“接收者”保持的变量包括以下的一个或多个:“Object?#24065;?#29992;、静态
类型的“Object?#24065;?#29992;(“Object”的子类型)、“整型?#34987;頡?#38271;整型”。然而,其它实施例可以具有
可以保持许多其它类型的原语(primitive)变量的“接收者?#20445;?#35832;如浮点型、双精度型、?#22336;?br />型等?#21462;?#21478;外,其它实施例可以提供对于保持“类似原语”变量(诸如?#22336;?#20018;)的“接收者”的
支持。此外,在其它实施例中,由“接收者”保持的变量可以表示在堆302外部或者甚至在运
行?#34987;?#22659;112外部的存储器区域。例如,VarHandle可以表示对不同虚拟机/运行?#34987;?#22659;的存
储器区域中的结构的引用。为了清晰,斜体的Object指的是充当所有其它类的超类的泛型
对象结构,正如在Java编程语言中那样。其它对象类型被称为“静态类型的对象”并且是
Object的子类。

在一些实施例中,VarHandle对保?#31181;?#31867;型的接收者执行受约束的操作,如在2014
年5月13日提交的美国临时申请61/992,753中所描述的那样,在此出于所有目的通过引用
并入该申请,如同本文完全阐述了该申请一样。

在示例实施例中,假设存在三种类型的接收者和四种类型的变量,对于每个受约
束的操作,实现的数量为12(3×4)。例如,处理对Object的静态字段、静态类型对象的数组、
整型的实例字段以及各种组?#31995;?#30340;受约束访?#23454;?#23454;现。接收者类型和变量类型的组合将被
称为“访问类型”。此外,在示例实施例中存在9(3×3)个不同的接口形状,因为Object引用
和静态类型对象引用可以共享公共的接口形状(可以使用泛型Object类型来表示二者),但
是具有不同的实现。例如,后者可以执行?#20801;?#30340;类型转换,而对于前者来说类型转换将是多
余的。然而,实现和/或接口形状的数量取决于由给定实施例支持的不同访问类型的数量。
此外,假设存在八种不同类型的支持的受约束操作,则示例实施例将拥有96(8×12)种不同
的受约束操作实现。例如,其中接收者是静态字段并且变量是静态类型对象的放宽的获得
的实现、其中接收者是实例字段并且变量是整型的?#36164;?#24615;设置的实现,以及各种组?#31995;取?br />

在一些实施例中,使用用于每个接口形状的分开的抽象类来实现VarHandle,从而
造成九个不同抽象类的集合。然后这些抽象类可以由实现用于给定访问类型的受约束操作
方法(包括用于其的代码)的类来进行子类化(sub-class)。例如,一个子类可以实现接收者
是数组并且变量是泛型Object的情况,而另一个子类可以实现接收者是数组并且变量是静
态类型对象的情况。二者共享相同的接口形状并且因此可以派生自同一抽象类,但是提供
不同的实现。然而,为了简化对VarHandle的访问,一些实施例为VarHandle定义包括用于每
个受约束操作的多态方法签名的单个抽象类。例如,在Java代码中,抽象类可以部分地看起
来是:

public abstract class VarHandle{

...

//放宽的访问器

public final native

@MethodHandle.PolymorphicSignature

Object get(Object...args);

public final native

@MethodHandle.PolymorphicSignature

Object set(Object...args);

//?#36164;?#24615;访问器

public final native

@MethodHandle.PolymorphicSignature

Object getVolatile(Object...args);

public final native

@MethodHandle.PolymorphicSignature

Object setVolatile(Object...args);

//懒惰的访问器

public final native

@MethodHandle.PolymorphicSignature

Object getAcquire(Object...args);

public final native

@MethodHandle.PolymorphicSignature

Object setRelease(Object...args);

//比较和设置访问器

public final native

@MethodHandle.PolymorphicSignature

Object compareAndSet(Object...args);

public final native

@MethodHandle.PolymorphicSignature

Object getAndSet(Object...args);

...}

以下示例引用VarHandle以便于说明清楚的示例,但是本文所描述的技术不限于
如在VarHandle示例中呈现的类的确切格式。

在实施例中,多态签名是指示方法可以接受任何?#25105;?#25968;量的变元和变元类型以及
可以具有任何返回类型的特殊类型的签名。因此,实现对应于多态签名的方法的VarHandle
的子类可以潜在地使用任何接口形状并且仍然通过验证。因此,一个抽象类足以覆盖不同
的访问类型,并且开发者可以以相对统一的方式利用VarHandle的任何子类的受约束的操
作方法。

在实施例中,VarHandle子类保持与受约束操作的四个可能方法描述符对应的四
个方法类型描述符以及保持“?#31245;?#21517;(membername)”的“varform”。例如,用于“-get(获得)”
操作的描述符可以是相同的,类似地用于“-set(设置)”操作的描述符也可以是相同的,因
为这些方法的变元和返回类型匹配。由VarHandle子类保持的方法类型描述符决定所实现
的受约束操作方法的方法签名。因此,为了正确地使用受约束操作方法,调用点使用由对应
描述符指定的变量和返回类型来启用该方法。“varform”表示不同受约束操作的行为。因
此,在示例实施例中,varform保持八个“?#31245;?#21517;?#20445;?#27599;个?#31245;?#21517;表征不同的受约束操作方法
的行为。例如,对于每个受约束操作,对应的“?#31245;?#21517;”可以表示用于执行用于所实现的访问
类型的受约束操作的代码或指令。

3.3VarHandle过程流程字段例

图5示出了根据实施例的以框图?#38382;?#30340;用于使用VarHandle执行受约束操作的示
例过程。为了本节的目的,利用VarHandle的类将被称为“作用类(acting class)?#20445;?#24182;且将
假设使用示例VarHandle抽象类来定义VarHandle。

在下面的示例中,将参考访问类的实例字段的VarHandle来描述图5。针对数组访
问使用图5的过程的附加示例将稍后在节3.4中描述。

在方框500处,虚拟机104接收VarHandle实例的声明。在一些实施例中,虚拟机104
经由已从用于作用类的?#21019;?#30721;文件101编译而来的类文件103接收VarHandle实例的声明。
在实施例中,可以使用以下示例?#21019;?#30721;来声明用于由接收者“Receiver”保持的类型
“Value”的字段“val”的VarHandle实例:

VarHandle varHandleOfValueOnReceiver=VarHandles.lookup()

.findFieldHandle(Receiver.class,"val",Value.class);

在实施例中,VarHandles是如下生成器类:其包括用于返回实现用于特定访问类
型的受约束操作的各种VarHandle子类的实例的方法。lookup()是类VarHandle的静态方
法,它返回被用于返回(经由方法findFieldHandle)连结到(tiedto)指定字段的VarHandle
子类实例的查找类。例如,由查找类生成的VarHandle子类实例可以存储与VarHandle子类
实例所绑定到的接收者和变量类对应的用于每个方法的描述符。在上面的示例声明中,
findFieldHandle创建访问具有Receiver类型的静态类型对象的名为“val”的字段的
VarHandle实例,其中该字段保持具有Value类型的静态类型对象。因此,例如,对于指派给
varhandleOfValueOnReceiver的VarHandle来说,“-set”操作可以与描述符“(L Receiver
L Value)V”相关联,而“-get”操作可以与描述符“(L Receiver)L Value”相关联。

作为另一个示例,在用于类的静态字段的VarHandle的情况下,查找类方法可以采
用类的名称(或者表示该类的Class对象的名称)、在该类内的静态字段的名称以及静态字
段的类。在一些实施例中,查找类执行访问控制检查,以查明作用类是否具有访问指定的接
收者和变量的?#23454;?#29305;权。例如,如果变量被声明为?#25509;?#30340;,则在定义该变量的包外部的类可
能没有足够的特权来访问该变量,这将导致查找类生成错误。在一些实施例中,在创建
VarHandle时而不是在启用VarHandle的方法时执行访问检查。在一些情况下,在查找时执
行访问控制检查通过允许VarHandle在启用期间省略这些访问检查来减少开销。因此,在这
样的实施例中,有可能将VarHandle传递到否则将不具有对所引用的接收者/变量的访问许
可的类。然而,在一些实施例中,在启用期间可以再次执行访问检查,以防止具有不足的许
可的类启用VarHandle,其代价是低效的运行时性能。

在方框501处,虚拟机104接收使用VarHandle执行受约束操作的指令。例如,对
varHandleOfValueOnReceiver的?#36164;?#24615;设置操作可以看起来如下:

Receiver r=...

Value v=...

varHandleOfValueOnReceiver.setVolatile(r,v)

其中r是类Receiver的实例,v是类Value的实例,并且使用r和v为?#38382;?#23545;
varHandleOfValueOnReceiver启用方法setVolatile。在实施例中,当编译器102构建用于
作用类的类文件200?#20445;?#24120;量表201的方法引用结构207包括对类VarHandle、方法的名称
setVolatile以及表示采用类型为Receiver的对象和类型为Value的对象作为?#38382;?#24182;且具
有返回类型void的方法的符号方法描述符“(L Receiver L Value)V”的引用。此外,在虚拟
机104代码中,调用被转换为指定对于上面提到的对setVolatile的方法引用的到常量表
201的索引的启用指令。

作为另一个示例,在用于类的静态字段的VarHandle的情况下,setVolatile可以
被隐式地绑定到类,从而消除了?#20801;?#25351;定接收者的需要。例如,调用可以看起来是
“varHandleOfValueOnReceiver.setVolatile(v)”。在声明期间,VarHandle被绑定到接收
者的类/类型,并且因为这是静态访问,所以仅存在一个可能的接收者(该类自身)。因此,与
实例字段情况不同,VarHandle已经知道要对其作用的确切接收者。然而,在实例字段情况
下,VarHandle被绑定到接收者的类型/类,但是不绑定到任何特定的接收者实例。因此,调
用特定受约束函数/方法的指令通过隐式绑定到接收者(诸如在静态情况下)来指示接收者
或者指定接收者(诸如在实例字段的情况下)。因此,执行实例访?#23454;腣arHandle能够对被传
递到调用中的任何接收者实例执行操作,只要该接收者匹配在声明期间VarHandle被绑定
到的类型。

作为另一个示例,对varHandleOfValueOnReceiver的?#36164;?#24615;获得操作可以看起来
如下:

Receiver r=...

Value v=(Value)varHandleOfValueOnReceiver.getVolatile(r);其中r是
Receiver的实例,v是值的实例,(Value)是对返回的类型转换,并且使用r作为?#38382;?#23545;
varHandleOfValueOnReceiver启用getVolatile。在实施例中,当编译器102构造用于作用
类的类文件200?#20445;?#24120;量表201的方法引用结构207包括对类VarHandle、方法的名称
getVolatile以及表示采用类型为Receiver的对象作为?#38382;?#24182;且返回类型为Value的对象
的方法的符号方法描述符“(L Receiver)L Value”的引用。此外,在虚拟机104代码中,调用
被转换为指定对于上面提到的对getVolatile的方法引用的到常量表201的索引的启用指
令。在一些实施例中,getVolatile将静态类型的对象向下擦除(erase down)为最一般的形
式(类型Object),因此返回时的类型转换确保编译器102可以确定用于符号方法描述符“(L
Receiver)L Value”的返回类型。然而,在其它实施例中,如果编译器102可以从表达式左侧
的局部变量类型推断出返回类型(例如,编译器实现“目标类型化”),则可以避免?#20801;?#30340;类
型转换。

在方框502处,虚拟机104对受约束操作执行类型安全检查。在实施例中,?#24065;?#29992;多
态签名方法的启用指令由虚拟机104执行?#20445;?#29992;被称为“内部链接”的过程来替换上文在节
2.3中描述的链接步骤。在正常情况下,使用?#36164;?#24615;设置操作作为示例,当虚拟机104试图解
析符号引用却发?#32622;?#26377;与名称setVolatile和描述符“(L Receiver L Value)V”相匹配的
方法在类VarHandle中被定义?#20445;?#23558;发生链接错误。然而,响应于确定被启用的方法是签名
多态的,虚拟机104在执行各种类型安全检查的调用点处执行与启用的访问类型相关联的
预定义方法,并且执行与被启用方法对应的?#31245;?#21517;。

例如,多态签名方法可以与标志或者属性的特定组合相关联,诸如在VarHandle类
的类文件200中将方法设置为最终的(final)和本机的(native)二者。当启用方法引用?#20445;?br />虚拟机104检查标志,以确定该方法是否是签名多态的。如果是,则虚拟机104调用被设计为
处理由方法引用的描述符指定的特定接口形状的预定义方法。例如,预定义方法可以将类
向下擦除为泛型Object,以便通过允许向下擦除为同一接口形状的不同启用使用同一预定
义方法来减少预定义方法的数量。否则,方法不是签名多态的,并且虚拟机104继续进行如
上文在节2.3中所描述的链接。

在一些实施例中,虚拟机104操纵操作数栈402,以向预定义方法提供附加信息。例
如,由于在内部链接期间绕过了平常的链接程序,因此不能保证提供给被启用的多态签名
方法的变元匹配在VarHandle声明期间指定的类型。这可能导致非预期的行为,因为被启用
以执行受约束操作的VarHandle实例的?#31245;?#21517;可能?#35272;?#20110;在VarHandle声明期间指定的接
收者和变量类的大小和/或访问类型。在启用setVolatile方法?#20445;?#25805;作数栈402将具有被推
送到其上的r、v和varHandleofValueonReceiver。然而,虚拟机104可以将诸如对由启用指
令索引的方法引用结构的描述符的引用之类的附加数据推送到栈上。因此,在预定义的方
法中,虚拟机104可以将由VarHandle实例存储的描述符与调用点处的方法启用的描述符进
行比较,以确保变元和返回类型与VarHandle实例的?#31245;?#21517;所期望的变元和返回类型匹配。
如果匹配,则预定义方法启用?#31245;?#21517;以执行受约束的操作。否则,生成错误。

因此,在一些实施例中,内部链接分两个部?#31181;?#34892;:(1)虚拟机104基于方法引用的
被擦除的描述符来自动启用预定义方法,以执行类型安全检查,以及(2)虚拟机104启用
VarHandle实例的?#31245;?#21517;,以执行受约束的操作。在一些实施例中,在步骤(2)期间,执行附
加的安全检查,诸如确定接收者是否为null或者(在数组访?#23454;?#24773;况下)确定指定的索引是
否在界限内。

当句柄被常量折叠(constant folded)?#20445;资?#24615;设置操作的示例内联踪迹
(inlining trace)如下:


另外,用于处理“(L Object,L Object)V”的被擦除的签名的用于setVolatile的
示例预定义方法可以看起来如下:


在示例方法setVolatile_LL_V中,第一?#38382;?#26159;作为类VarHandle的实例的handle,
第二?#38382;?#26159;接收者,第三?#38382;?#26159;值,而最后一个?#38382;?#26159;符号方法描述符。示例方法
setVolatile_LL_V首先执行方法类型描述符检查,以确定在调用点处的符号描述符(在作
用类处的方法调用)是否与用于由handle存储的用于“-set(设置)”操作的调用描述符匹
配。如果描述符不匹配,则生成错误,诸如抛出将由异常处理程序捕获的异常。否则,将利用
除了在内部链接期间被添加的symbolicMethodType之外的所有?#38382;?#26469;启用
MethodHandle.linkToStatic方法以辅助预定义方法。然而,附加?#38382;?br />handle.vform.setVolatile被添加到变元,它表示实现受约束的操作的?#31245;?#21517;。try/catch
块存在,以捕获可能由MethodHandle.linkToStatic启用抛出的异常。然而,在其它实施例
中,可以通过利用避免这样的声明的内部链接方法来避免try/catch块。在实施例中,对内
部链接方法使用@ForceInline,它通知解释器108的JIT编译器?#36824;?#26368;大内联限制和方法大
小如何都内联该方法。

在方框503处,虚拟机104链接到实现受约束操作的方法代码。在实施例中,方法
MethodHandle.linkToStatic链接由与?#36164;?#24615;设置相关联的“?#31245;?#21517;”表征的方法(在上面
的示例中为handle.vform.setVolatile)。因此,MethodHandle.linkToStatic使得虚拟机
104执行解析,从而将作用类中对setVolatile的方法引用解析为处理所声明的varHandle
的访问类型的子类的setVolatile方法代码。在这种情况下,示例声明使用指派给
VarHandle子类型FieldInstanceRefHandle的lookup()方法并且因此linkToStatic将引
用链接到实现受约束操作setVolatile的FieldInstanceRefHandle的方法代码。对于字段
实例访问实现setVolatile的链接代码的示例如下:



第一个?#38382;?#26159;“FieldInstanceRefHandle”实例而后续?#38382;?#26159;传递给
“VarHandle.setVolatile”方法的?#38382;!癋ieldInstanceRefHandle”实例保持要与
“Unsafe.putObjectVolatile”的启用一起使用的字段偏移。在该启用之前:执行安全检查,
以确保接收者实例不是“null”(以避免潜在的分?#26410;?#35823;);并且作为附加的安全检查来执行
值的转换检查。

在方框504处,虚拟机104执行所链接的方法代码,以执行受约束操作。在上面的示
例中,所链接的方法代码经由对Unsafe.putObjectVolatile的调用来执行?#36164;?#24615;设置操
作,其中Unsafe.putObjectVolatile以指定的受约束方式将value存储在对应的存储器位
置处。例如,虚拟机104可以经由对操作系统110的一个或多个系统调用或呈?#25351;?#24213;层计算
机系统的处理器的机器指令来执行受约束操作。例如,为了实现存储器围栏操作,除了对变
量的存储器位置的加载和/或存储之外,虚拟机104还可以发出由底层硬件本机支持的一条
或多条存储器屏障指令。在一些实施例中,底层计算机硬件的处理器可以不支?#31181;?#34892;受约
束操作的本机操作。然而,在这样的实施例中,虚拟机104可以将综合了受约束操作将产生
的相同效果的多个系统调用或机器指令?#21019;?cobble)在一起。

3.4VARHANDLE过程流数组例

以下是被应用于接收者是数组并且变量是静态类型对象的情况的图5的过程流的
示例。为了本节的目的,利用VarHandle的类将被称为“作用类?#20445;?#24182;且将假设使用示例
VarHandle抽象类来定义VarHandle。

在方框500处,虚拟机104接收VarHandle的声明。在实施例中,使用以下示例?#21019;?br />码来声明由数组类型“Value[]”的接收者保持的组件类型“Value”的数组元素的
VarHandle实例:

VarHandle varHandleOfValueArray=VarHandles.

arrayHandle(Value[].class);

其中VarHandles.arrayHandle返回被实现以处理数组访?#23454;腣arHandle子类的实
例。

在方框501处,虚拟机104接收经由VarHandle执行受约束操作的指令。在实施例
中,经由以下示例?#21019;?#30721;来启用对数组类型Value[]的实例r的索引i处的数组元素Valuev
的?#36164;?#24615;设置操作:

Value[]r=...

int i=...

Value v=...

varHandleOfValueArray.setVolatile(r,i,v)

在实施例中,对setVolatile(r,i,v)的方法引用包括方法描述符
“(LReceiverILValue)V?#20445;?#20854;表示该方法采用类型为Receiver的对象、整数和类型为Value
的对象,并且返回类型为void。

在实施例中,通过以下示例?#21019;?#30721;来启用对来?#20801;?#32452;类型Value[]的实例r的索引
i处的数组元素的Valuev的?#36164;?#24615;获得操作:

Value[]r=...

int i=...

Value v=(Value)varHandleOfValueArray.getVolatile(r,i);

对应的符号方法描述符是“(LReceiverI)LValue?#20445;?#20854;匹配?#36164;?#24615;获得操作的方法
类型描述符。

在方框502处,虚拟机104对该操作执行类型安全检查。在实施例中,对
“VarHandle.setVolatile”的启用内部链接到“VarHandle.setVolatile_LIL_V?#20445;?br />



在VarHandle.setVolatile_LIL_V中,类型安全检查由checkExactType执行,其确
保handle的方法描述符匹配在调用点处使用的?#38382;?返回值的symbolicMethodType。

在实施例中,“MethodHandle.linkToStatic”的启用链接?#25509;?#19982;?#36164;?#24615;设置相关
联的“?#31245;?#21517;”表征的方法。在这种情况下,handle经由Varhandles.arrayHandle生成,
Varhandles.arrayHandle返回VarHandle的ArrayRefHandle子类型并且因此?#31245;?#21517;链接到
setVolatile方法的数组实现。在实施例中,所链接的方法的示例实现如下:



“ArrayRefHandle”实例保持数组基地址偏移和数组移位,以便从索引计算要与
“Unsafe.putObjectVolatile”的启用一起使用的偏移。在该启用之前:执行安全检查,以确
保数组不为“null”并且索引在数组界限内。

3.5优化

在实施例中,虚拟机104执行用于VarHandle的优化,以减轻由于内部链接、类型检
查和安全检查而施加的负担。

作为一个示例,可以由虚拟机104执行的优化是“常量折叠(constant folding)”。
常量折叠是在编译时而不是在执行期间评估涉及常量的表达式的优化技术。因此,例如,解
释器108/JIT编译器可以用存储结果的指令替换评估常量的指令。例如,“i=100+105+109”
一般会把值推送到栈上并且执行相加指令的启用。然而,虚拟机104可以替代地提前评估表
达式并且用将“314”加载到局部变量i中的单个指令来替换这些指令。因此,当虚拟机104指
令被转换为机器代码以供执行?#20445;?#30001;于执行更少?#36879;?#31616;单的操作,所以运行时速度增加。

作为另一个示例,可以由虚拟机104执行的优化是“内联?#34987;頡?#20869;联展开”。在对虚拟
机104或其组件(诸如解释器108/JIT编译器)进行内联期间,用被调用方法的主体来替换在
调用点处的方法启用。因此,虚拟机104绕过了处理该启用以及使执行从一个方法的主体的
跳转到另一个方法的主体的开销。在实施例中,当虚拟机104将调用内部链接到对
VarHandle的受约束操作?#20445;?#34394;拟机104可以通过内联在内部链接期间执行的代码中的一些
或全部来优化。

作为另一个示例,可以由虚拟机104执行的优化是高速缓存所解析的VarHandle启
用并且对于将来的启用重用该存储器位置。假设识别绑定的接收者和变量类型VarHandle
的字?#38382;?#38745;态的,则不需要再次执行在查找期间的访问许可检查以及由预定义方法执行的
类型安全检查。因此,当下次启用同一VarHandle方法?#20445;?#21487;以跳过这些检查,从而得到?#36127;?br />与直接暴露“不安全”方法持平的运行时性能。

作为另一个示例,实施例可以将对VarHandle的引用声明为一旦设置就不能改变
的常量值。例如,在Java中,一些实现使用“static final(静态最终)”属性来定义这样的字
段。因此,使用Java示例,对VarHandle实例“V”的static final引用被认为是常量,因此关
于可以经由引用?#35272;?#20851;系链被追溯到V的类的任何final实例字?#25105;?#34987;认为是常量。因此,
当虚拟机104内联对V的方法调用?#20445;?#34394;拟机104可以将与类型检查相关联的指令常量折叠
起来(fold away),link-to-static(链接到静态)调用可以被内联(因为已知最后一个变元
是常量),并且?#20801;?#30340;转换检查也可以折叠起来。

3.6放宽关于多态签名方法的返回类型

在一些实施例中,可以放宽多态签名方法(诸如在先前的VarHandle示例中所使用
的多态签名方法)的返回类型,以使得如果类型不是泛型Object,则该类型是被编码成用于
该方法的符号类型描述符的返回类型。例如,在说明性实施例中,VarHandle类可以被修改
以声明放宽的多态签名方法,以使得所有基于设置的方法都返回“void?#20445;?#32780;比较和设置方
法返回?#23433;级?#22411;”。因此,在一些情况下,因为返回类型可以从方法签名推断出,所以可以避
免上面的示例中的类型转换。表示用于VarHandle的放宽的多态方法签名的示例?#21019;?#30721;部
分如下:

abstract class VarHandle{

...

public final native

@MethodHandle.PolymorphicSignature

voidset(Object...args);

...

public final native

@MethodHandle.PolymorphicSignature

boolean compareAndSet(Object...args);

...

}

3.7通用多态签名方法

在实施例中,多态签名方法的类型的放宽被扩展,以换取每个接收者类型声明一
个抽象类。因此,这样的抽象类将能够支持原语类型和引用值类型二者。

在实施例中,作为一个VarHandle抽象类的代替,定义对应于每种类型的接收者的
三个类:

-用于静态字段访?#23454;摹癝taticFieldHandle<R,V>”

-用于实例字段访?#23454;摹癋ieldHandle<R,V>?#20445;?#21644;

-用于数组元素访?#23454;摹癆rrayHandle<R,I,V>”。

为接收者R和值V声明类型变量。除了数组访问之外,还存在用于到数组的索引I的
类型变量。在一些实施例中,类型变量I确保针对大于由虚拟机104定义的最大整数值的索
引的对大数组中的元素的访问可以被支持。此外,与VarHandle一样,用于数组的接收者可
以是数组类,或者可以是用于类似数组的结构的某种其它类型,诸如对堆外存储器的访问。

在静态字段访问可能很少的一些实施例中,这样的访问可以被折叠成
“FieldHandle<R,V>?#20445;?#20854;中受约束的访问方法接受空值或忽略接收者的值。因此,用于接收
者的空值可以充当指示是否对所声明的VarHandle使用静态字段访问或实例字段访?#23454;?#26631;
志。因此,可以以用于确定是否正在执行静态访问或字段访?#23454;?#38468;加处理为代价来减少类
的数量。

在一些实施例中,上文描述的类可以从VarHandle扩展,以保留较低的级别但是更
通用的机制,因为关于子类的方法将实质上重写超类的那些方法。

在实施例中,方法对于受约束操作通过其被内部链接的机制保持与先前在节3.3
中描述的相同,并且因此性能将保持与VarHandle示例相对持平。然而,在一些情况下,可用
性可以被增强,因为在编译时而不是运行时可能捕获更多的错误,这是由于在一些实?#31181;校?br />签名多态方法的启用可以由编译器102编译,而?#36824;?#29992;于该启用的描述符是否产生?#34892;?#30340;
实现方法。

考虑用于对访问类型为Receiver的实例上的int字段的FieldHandle的启用的以
下示例代码:

FieldHandle<Receiver,Integer>fh=...

int a=1;

int b=2;

fh.compareAndSet(this,1,2);

Integer ba=a;

Integer bb=b;

fh.compareAndSet(this,ba,bb);

在该示例中,原语类型被表示为装箱的(boxed)类型。在表示原始值的对象中封装
(package)原始值被称为“装箱”。同样,将装箱的?#21040;?#21253;回原始?#38382;?#34987;称为?#23433;?#31665;”。在一些
实施例中,诸如整型、?#32440;?#22411;、?#22336;?#22411;、长整型等之类的原语类型不能被擦除为较通用的形
式。然而,装箱的?#38382;?#21487;以被擦除为泛型Object。因此,通过将原始值装箱,原始值可以被一
般化。

在一些实施例中,第一个“compareAndSet”操作编译为以下示例虚拟机104代码:

10:invokevirtual#2//Method java/lang/invoke/
FieldHandle.compareAndSet:(LVarHandleT est;II)Z

并且第二个编译为:

32:invokevirtual#4//Method java/lang/invoke/
FieldHandle.compareAndSet:(LVarHandleT est;Ljava/lang/Integer;Ljava/lang

其中第一个数字(“10”/“32”)表示虚拟机104代码的索引,invoke virtual是启用
采用指示常量表201中的方法引用的索引的变元的实例方法的命令,并且注释包括包含该
方法的类的完全限定的路径名称和正在被启用的方法的描述符。

由于原语类型“int”与它的装箱的类型“Integer”兼容,因此编译器102将成功地
编译第一启用,并且因此符号方法描述符将等于比较和设置操作的方法描述符(例如,在方
法是签名多态的情况下,编译器103将验证“int”可转换为“Integer?#20445;?#20294;是将不生成装箱指
令)。对于第二次启用,符号方法描述符将不同,并且因此可能导致运行?#24065;?#24120;。然而,
invoke-exact语义可以被放宽,以替代地对变元执行变换,以便使得在调用点处的描述符
与用于比较和设置操作的描述符相匹配。例如,值可以在执行基本启用之前和/或之后被自
动地装箱或拆箱。

在一些实施例中,变换被扩展到接收者类型和/或非原始值类型,诸如从?#22336;?#25968;组
转换而来的?#22336;?#20018;。另外,在一些实施例中,编译器102将类型?#38382;?#30340;类(如果已知的话)编
码为符号方法描述符,而不是可能的子类型。

在实施例中,VarHandle实例的方法中的一个或多个方法被定义为具?#22411;?#29992;签名,
并且虚拟机104为例如编译后的Java?#32440;?#30721;中的VarHandle实例的方法中的一些或所有方
法在调用点处具体化(reify)通用签名。

例如,考虑以下Java代码:


上面的类中的fh.compareAndSet的启用可以被编译为以下指令:

32:invokevirtual#6//方法

Test$FieldHandle.compareAndSet:(Ljava/lang/Object;

Ljava/lang/Object;Ljava/lang/Object;)Z

在上面的示例中,与类型变量“R”和“V”相关联的?#38382;?#31867;型(在这种情况下是用于
启用的Receiver和Value的实例)被擦除为泛型Object。在具体化通用签名被实现的实施例
中,Receiver和Value可以在调用点处被捕获,而不是擦除它们,并且如果知道被传递给该
方法的?#38382;?#30340;类型是正确的,则在进一步继续之前,可以执行调用点处的方法签名(符号类
型描述符)与期望的方法类型描述符的高效的指针相等?#32422;?#26597;(就像在多态签名的情况下
那样)。

3.8代码VARHANDLE和方法句柄的合并

在一些实施例中,除了VarHandle之外,虚拟机还实现充当对特定方法的句柄的、
被称为MethodHandle的另一访问结构。MethodHandle实现的示例可以在公开的Java文档中
找到。(http://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodH
andle.html)

与VarHandle一样,MethodHandle的一些实施例利用多态签名和内部链接。例如,
启用MethodHandle被绑定到的方法可以通过对被声明为使用lookup类的特定方法的
MethodHandle实例使用.invoke([Parameters])来执行。启用内部链接到MethodHandle的
LamdaForm,该MethodHandle的LamdaForm本质上充当实现方法的包装器,并?#20197;?#21551;用实现
方法之前执行各种动作和/或检查。

在一些实施例中,作为VarHandle保持Varform的代替,VarHandle保持对应于不同
的受约束操作方法的LambdaForms的集合。因此,“VarForm”实例可以被高速缓存并?#20197;?br />VarHandle和MethodHandle实例之间共享。此外,常见的“VarForm”实例可以静态定义并且
因?#31169;档推?#21160;成本。LambdaForm本质上充当其引用可以被更新的?#31245;?#21517;的箱子。当
LamdaForm从被解释切换到(经由编译器102)被编译?#20445;?#21487;能发生更新。具体而言对于静态
字段访问,?#31245;?#21517;指向如下实现:其检查类是否需要被初始化(并且如果需要的话就将类初
始化)、访问静态字段,然后将?#31245;?#21517;更新为不执行静态访问并且仅访问该字段的实现。此
外,一些实施例可以实现用于返回对于特定受约束操作的MethodHandle的VarHandle的方
法。

在一些实施例中,作为可替代物,可以生成参考VarHandle实例上的方法的基于字
段的直接方法句柄,以便以新?#38382;?#30340;类自旋为代价减少自旋(spinning)类的数量。

3.9存储器围栏

存储器围栏是使得诸如CPU之类的处理器或编译器对在屏障指令之前和之后发出
的存储器操作实施排序约束的一种类型的屏障指令。因此,在该屏?#29616;?#21069;发出的操作被保
证在该指令之后发出的操作之前执行。采用存储器围栏的一个原因是由于CPU和编译器常
常采用可能导致无序执行的性能优化的事实。对于单个执行线程来说,优化通常被设计为
确保程序的语义不受干扰,诸如省略对于从未由程序实际使用的数据的加载指令或者从缓
冲区或高速缓存而不是从主存储器检索值。然而,对于多线程程序来说,除非仔细控制,否
则存储器操作的重新排序可能导致不?#31245;?#27979;的行为。存储器围栏不一定将数据存储或加载
到存储器位置,而是控制?#38382;?#21487;以从缓冲区/高速缓存读取值以及?#38382;被?#20914;区/高速缓存应
当被冲刷到主存储器。

?#36127;?#25152;有处理器至少支持保证在围栏之前发起的所有加载和存储将被严格排序
在围栏之后发起的任何加载或存储之前的通用屏障指令。然而,许多处理器还支持提供较
少排序保证但是常常产生较少开销的更细粒度的屏障。

在实施例中,虚拟机104支持可以访?#23454;?#23618;硬件架构(或虚拟化的软件构造
(construct),诸如虚拟机104)的各种防护操作的VarHandle。例如,VarHandle可以被扩展
为包括附加的方法,该方法作为调用执行原子操作的“不安全”方法的代替内部链接到发出
屏障指令的?#31245;?#21517;。由VarHandle支持的确切的屏障指令可以由硬件的底层架构决定。例
如,底层处理器或多处理器可以支持等同于所期望的存储器围栏的机器级指令。然而,在其
它实施例中,底层硬件可能不支持所期望的存储器围栏。因此,在一些实施例中,虚拟机104
通过在软件中实现围栏来合成存储器围栏操作,从而基本上使用其它机器级指令的组合来
实现相同的效果。

以下是可以经由VarHandle访?#23454;?#23384;储器围栏的几个示例。

(1)LoadLoad(加载加载)屏障–序列(Load1;LoadLoad;Load2),确保Load1的数据
在Load2访?#23454;?#25968;据和所有后续加载指令被加载之前被加载。在一些实施例中,LoadLoad屏
障被用于执?#22411;?#27979;加载和/或其中等待加载指令可以绕过等待存储的无序处理的处理器。

(2)StoreStore(存储存储)屏障–序列(Store1;StoreStore;Store2),确保Store1
的数据在与Store2和所有后续存储指令相关联的数据之前对其它处理器可见。例如,常常
对否则不保证?#26377;?#32531;冲区和/或高速缓存到其它处理器或主存储器的冲刷的严格排序的处
理器使用StoreStore屏障。

(3)LoadStore(加载存储)屏障–序列(Load1;LoadStore;Store2),确保Load1的数
据在与Store2和所有后续存储指令相关联的所有数据之前被加载。例如,常常对否则不保
证?#26377;?#32531;冲区和/或高速缓存到其它处理器或主存储器的冲刷的严格排序的处理器使用
StoreStore屏障。

(4)StoreLoad(存储加载)屏障–序列(Store1;StoreLoad;Load2),确保Store1的
数据在由Load2和所有后续加载指令访?#23454;?#25968;据被加载之前对其它处理器可见。StoreLoad
屏障防止后续的加载不正确地使用Store1的数据值而不是使用来自由不同处理器执行的
对同一位置的更近存储的数据值。

在一些实施例中,虚拟机104在底层硬件?#26087;?#19981;支持围栏的情况下利用支持的屏
障指令来合成围栏。例如,假设底层硬件不提供对于释放设置和获取获?#32654;?#24816;操作的本机
支持,而是支持上文列出的示例屏障指令。可以通过将加载-加载屏障指令与加载-存储屏
障指令组合来合成获取围?#31119;?#24182;且可以通过将存储-加载屏障指令与存储-存储屏障指令组
合来合成释放围栏。在一些实施例中,上面提到的获取/释放围栏然后可以向硬件发出和/
或被用来实?#36136;?#25918;设置/获取获?#32654;?#24816;的受防护的操作。

3.10VARHANDLE功能的扩展

虽然VarHandle已经被描述为提供对原子操作和存储器屏障指令的高效和安全访
问,但是VarHandle不限于这些功能。在其它实施例中,VarHandle可以被扩展,以提供对几
乎任何类型的功能的访问,诸如对没有落入原子操作和存储器屏障指令的类别内的硬件特
征的访问。

4.0硬件概述

根据一个实施例,本文所描述的技术由一个或多个专用计算设备实现。专用计算
设备可以被硬连线以执行技术,或者可以包括诸如被永久性地编程以执行技术的一个或多
个专用集成电路(ASIC)或现场可编程门阵列(FPGA)的数字电?#30001;?#22791;,或者可以包括被编程
为按照固件、存储器、其它存储装置或者其组合中的程序指令来执行技术的一个或多个通
用硬件处理器。这样的专用计算设备还可以将定制的硬连线逻辑、ASIC或FPGA与定制的编
程组合来实现技术。专用计算设备可以是桌上型计算机系统、便携式计算机系统、手持式设
备、联网设备或者结合硬连线和/或程序逻辑以实现技术的任何其它设备。

例如,图6是示出可以在其上实?#30452;?#21457;明的实施例的计算机系统600的框图。计算
机系统600包括总线602或者用于传送信息的其它通信机制,以及与总线602耦接以用于处
理信息的硬件处理器604。硬件处理器604可以是例如通用微处理器。

计算机系统600还包括被耦接到总线602以用于存储信息和要由处理器604执行的
指令的主存储器606,诸如随机存取存储器(RAM)或其它动态存储设备。主存储器606还可以
被用于在要由处理器604执行的指令的执行期间存储临时变量或其它中间信息。当被存储
在处理器604可访?#23454;?#38750;暂态存储介质中?#20445;?#36825;样的指令使计算机系统600成为被定制以执
行指令中指定的操作的专用机器。

计算机系统600还包括耦接到总线602以用于为处理器604存储静态信息和指令的
只读存储器(ROM)608或者其它静态存储设备。诸如?#25490;獺?#20809;盘或固态驱动器之类的存储设
备610被提供并且耦接到总线602,以用于存储信息和指令。

计算机系统600可以经由总线602耦接到诸如发光二极管(LED)?#20801;?#22120;之类的?#20801;?br />器612,以用于向计算机用户?#20801;?#20449;息。包括字母数字和其它键的输入设备614被耦接到总
线602,以用于向处理器604传送信息和命令选择。另一种类型的用户输入设备是诸如鼠标、
轨迹球或者光标方向键之类的光标控制器616,以用于向处理器604传送方向信息和命令选
择以及用于控制?#20801;?#22120;612上的光标移动。这种输入设备通常具有允许设备指定平面中的
位置的在两个轴(第一个轴(例如,x)和第二个轴(例如,y))上的两个自由度。

计算机系统600可以使用与计算机系统结合使得计算机系统600成为专用机器或
者把计算机系统600编程为专用机器的定制的硬连线逻辑、一个或多个ASIC或FPGA、固件
和/或程序逻辑来实?#30452;?#25991;所描述的技术。根据一个实施例,本文的技术由计算机系统600
响应于处理器604执行包含在主存储器606中的一条或多条指令的一个或多个序列而执行。
这样的指令可以从诸如存储设备610之类的另一存储介质被读取到主存储器606中。包含在
主存储器606中的指令序列的执行使处理器604执行本文所描述的过程步骤。在可替代的实
施例中,硬连线的电路系统可以代替软件指令或者与软件指令结合使用。

如本文所使用的,术语“存储介质”指存储使机器以特定方式操作的数据和/或指
令的任何非暂态介质。这样的存储介质可以包括?#19988;资?#24615;介质和/或?#36164;?#24615;介质。?#19988;资?#24615;
介质包括例如光盘、?#25490;?#25110;固态驱动器,诸如存储设备610。?#36164;?#24615;介质包括动态存储器,诸
如主存储器606。存储介?#23454;?#24120;见?#38382;?#21253;括例如软盘、柔性盘、硬盘、固态驱动器、磁带,或者
任何其它磁?#20801;?#25454;存储介?#21097;珻D-ROM,任何其它光学数据存储介质、具有孔的?#21450;?#30340;任何物
理介质、RAM、PROM和EPROM、FLASH-EPROM、NVRAM、任何其它存储器芯片或盒式磁带。

存储介质与传输介质不同但是可以与其结合使用。传输介质参与在存储介质之间
传送信息。例如,传输介质包括同轴线缆、铜线和光纤,这些同轴线缆、铜线和光纤包括包含
总线602的配线。传输介质还可以采取声波或光波的?#38382;劍?#35832;如在无线电波数据通信和红外
数据通信中产生的那些声波或光波。

将一条或多条指令的一个或多个序列承载到处理器604以供执行可以涉及各种形
式的介质。例如,指令最初可以被承载在远程计算机的?#25490;?#25110;固态驱动器上。远程计算机可
以把指令加载到其动态存储器中并且使用调制解调器经电话线发送指令。计算机系统600
?#38236;?#30340;调制解调器可以接收电话线上的数据并且使用红外发射器把数据转换为红外信号。
红外检测器可以接收红外信号中承载的数据,而?#23454;?#30340;电路系统可以把数据放在总线602
上。总线602把数据承载到主存储器606,处理器604从主存储器606检索指令并且执行指令。
由主存储器606接收的指令可以可选地在由处理器604执行之前或之后被存储在存储设备
610上。

计算机系统600还包括耦接到总线602的通信接口618。通信接口618提供耦接到网
络链路620的双向数据通信,其中网络链路620连接到局域网622。例如,通信接口618可以是
提供到对应类型电话线的数据通信连接的综合业务数字网络(ISDN)卡、线缆调制解调器、
卫星调制解调器或者调制解调器。作为另一个示例,通信接口618可以是提供到兼容的局域
网(LAN)的数据通信连接的LAN卡。无线链路也可以被实现。在任何这样的实?#31181;校?#36890;信接口
618发送和接收承载表示各种类型的信息的数字数据流的电信号、电磁信号或光信号。

网络链路620通常通过一个或多个网络提供到其它数据设备的数据通信。例如,网
络链路620可以通过局域网622提供到主机计算机624或者?#25509;?#20114;联网服务提供商(ISP)626
运营的数据设备的连接。ISP 626又通过现在通常称为“因特网”628的全球分组数据通信网
络提供数据通信服务。局域网622和因特网628二者都使用承载数字数据流的电信号、电磁
信号或光信号。通过各种网络的信号以及在网络链路620上并且通过通信接口618的信号是
传输介?#23454;?#31034;例?#38382;劍?#20854;中这些信号把数字数据承载到计算机系统600或者承载来?#32422;?#31639;
机系统600的数字数据。

计算机系统600可以通过(一个或多个)网络、网络链路620和通信接口618发送消
息和接收包括程序代码的数据。在因特网示例中,服务器630可以通过因特网628、ISP 626、
局域网622和通信接口618发送对应用的请求代码。

接收到的代码可以由处理器604在它被接收到时执行、和/或被存储在存储设备
610或其它?#19988;资?#24615;存储装置中以供以后执行。

如本文所使用的,术语“第一”、“第二”、“某些”和“特定的”被用作命名约定,以将
查询、计划、表示、步骤、对象、设备或其它项彼此区分,使得这些项可以在它们被引入之后
被引用。除非本文另有指定,否则这些术语的使用并不暗示所引用的项的顺序、时机或任何
其它特点。

5.0扩展和可替代物

在前面的说明书中,参考依实施方式不同的许多具体?#38468;?#25551;述了本发明的实施
例。因此,什么是本发明以及申请人旨在将什么作为本发明的唯一且排他的指示物是以权
利要求发布的具体?#38382;?#20174;本申请发布的一组权利要求,包括任何后续补正。用于这些权利
要求中包含的术语的本文明确阐述的任何定义将决定权利要求中使用的这些术语的含义。
因此,没有在权利要求中明确陈述的限制、元素、性质、特征、优点或属性不应当以任何方式
限制这些权利要求的?#27573;А?#22240;而,说明书?#36879;酵加?#24403;在说明性的意义上而不是限制性的意
义上被考虑。

6.0附加公开

本文所描述的主题的方面在以下编号的条款中阐述:

1、一种方法,包括:识别创建变量句柄实例的第一指令,该第一指令包括识别接收
者的类型以及由接收者保持的该变量句柄实例被配置为提供对其的访?#23454;?#21464;量的声明信
息;响应于第一指令,并?#19968;?#20110;声明信息,执行一个或多个检查,以确定对变量的访问是否
是许可的;响应于确定对变量的访问是许可的,创建变量句柄实例,该变量句柄实例包括被
配置为对变量的存储器位置执行受约束操作的一个或多个约束函数;识别指定对该变量句
柄实例的一个或多个受约束函数中的特定受约束函数的调用的第二指令,其中第二指令指
示接收者;以及识别存储变量的特定存储器位置并且使该特定受约束函数相对于该特定存
储器位置被执行。

2、如条款1所述的方法,其中接收者是类并且变量是由该类保持的静态字段、接收
者是类实例并且变量是由该类实例保持的字段、接收者是数组并且变量是该数组的元素、
或者接收者是对表示变量的堆外或直接存储器位置的引用。

3、如条款1-2中的任何一个条款所述的方法,其中第一指令调用查找函数,该查找
函数使变量句柄实例存储变量在接收者内的偏移,并且将变量句柄实例的一个或多个受约
束函数绑定到识别接收者的类型和变量的类型的一个或多个描述符。

4、如条款1-3中的任何一个条款所述的方法,其中该方法由管理第一存储器区域
的虚拟机执行,并且变量的实例被存储在由虚拟机管理的第一存储器区域外部的第二存储
器区域中。

5、如条款1-4中的任何一个条款所述的方法,其中第一指令从特定类导出,并且确
定对由接收者保持的变量的访问是否是许可的是基于该特定类是否具有访问由接收者保
持的变量的许可。

6、如条款1-5中的任何一个条款所述的方法,还包括响应于确定对由接收者保持
的变量的访问是不许可的来生成访问错误。

7、如条款1-6中的任何一个条款所述的方法,其中该方法由虚拟机执行,并且虚拟
机通过生成由虚拟机正在其上执行的硬件支持的机器代码指令来执行受约束的操作。

8、如条款7所述的方法,其中受约束操作包括不由硬件?#26087;?#25903;持的至少一个受约
束操作,并?#19968;?#21253;括使用由硬件支持的机器代码指令的集合来合成受约束操作。

9、如条款1-8中的任何一个条款所述的方法,其中受约束操作执行以下操作中的
一个或多个:放宽的操作、?#36164;?#24615;操作、懒惰的操作、比较和设置操作或者获得和设置操作。

10、如条款1-9中的任何一个条款所述的方法,其中第二指令包括用于特定受约束
函数的一个或多个?#38382;?br />

11、如条款10所述的方法,其中该一个或多个?#38382;?#21253;括以下各项中的一个或多个:
变量的实例所位于的到接收者的实例的数组索引或者要存储到变量的实例的特定存储器
位置的值。

12、如条款1-11中的任何一个条款所述的方法,还包括:执行检查,以确保由第二
指令指定的接收者的实例与由声明信息识别的接收者的类型匹配。

13、如条款1-12中的任何一个条款所述的方法,其中使特定受约束函数相对于特
定存储器位置被执行包括经由一条或多条硬件指令调用与?#25105;?#23384;储器位置交互的函数。

14、如条款1-13中的任何一个条款所述的方法,其中与?#25105;?#23384;储器位置交互的函
数不执行类型检查。

15、如条款1-14中的任何一个条款所述的方法,其中变量句柄实例的一个或多个
受约束函数包括对应于一个或多个原子算术运算的函数。

16、如条款1-15中的任何一个条款所述的方法,其中变量句柄实例从扩展抽象变
量句柄类的变量句柄子类派生,该抽象变量句柄类为一个或多个受约束函数提供一个或多
个接口,并且变量句柄子类对特定访问类型实现一个或多个受约束函数。

17、如条款1-16中的任何一个条款所述的方法,还包括:响应于确定由第二指令调
用的特定受约束函数是签名多态的,基于表示对特定受约束函数的调用的引用的描述符来
自动启用预定义函数,其中该预定义函数执行类型检查并且启用执行该特定受约束函数的
变量句柄的函数。

18、如条款17所述的方法,其中确定特定受约束函数是签名多态的通过检查抽象
变量句柄类的运行时表示中的与该特定受约束函数相关联的一个或多个标志来执行。

19、如条款17-18中的任何一个条款所述的方法,其中作为基于声明信息创建变量
句柄实例的结果,类型检查通过将一个或多个?#38382;?#21644;对特定受约束函数的调用的返回类型
与和用于变量句柄实例的特定受约束函数相关联的描述符进行比较来执行。

20、如条款17-19中的任何一个条款所述的方法,其中启用变量句柄实例的特定受
约束函数至少包括将对常量池中的特定受约束函数的符号引用解析为表示运行时存储器
中的该特定受约束函数的位置的具体存储器引用。

21、如条款20所述的方法,还包括在运行时存储器中高速缓存特定受约束函数的
位置,以便在对该变量句柄实例的该特定受约束函数的未来调用期间重用。

22、如条款17-20中的任何一个条款所述的方法,其中变量句柄实例的特定受约束
函数至少执行一个或多个安全检查,该一个或多个安全检查如果成功,则使得访问该特定
存储器位置并且执行与该特定受约束函数对应的受约束操作的不安全函数被启用。

23、一种方法,包括:生成被配置为通过一个或多个存储器防护操作提供对存储器
的安全访?#23454;?#23545;象;通过该对象,接收指示存储器位置并且指定该一个或多个存储器防护
操作中的要相对于该存储器位置执行的特定存储器防护操作的调用;使得该特定存储器防
护操作相对于该存储器位置被执行;以及其中该方法由一个或多个处理器执行。

24、如条款23所述的方法,其中对象与定义一个或多个存储器防护操作的类相关,
但是不包括用于多个不同变量种类中的任何特定变量种类的一个或多个存储器防护操作
的实现。

25、如条款24所述的方法,其中所述多个不同变量种类包括以下的一个或多个:实
例字段变量、静态字段变量、数组元素变量或堆外变量。

26、如条款24-25中的任何一个条款所述的方法,其中对象是从该类扩展并且相对
于多个不同变量种类中的特定变量种类实现一个或多个存储器防护操作的子类的实例。

27、如条款24-25中的任何一个条款所述的方法,还包括:生成作为该类的实例的
第二对象;通过该第二对象,接收指示第二存储器位置并且指定一个或多个存储器防护操
作中的要相对于第二存储器位置执行的第二特定存储器防护操作的第二调用,其中存储器
中的存储器位置表示第一变量类型并且第二存储器位置表示第二变量类型,其中第一变量
类型不同于第二变量类型;以及使得特定存储器防护操作相对于第二存储器位置被执行。

28、如条款27所述的方法,其中第一变量类型和第二变量类型是以下的一个或多
个:整型、长整型、浮点型、双精度型或对象引用。

29、如条款23-28中的任何一个条款所述的方法,其中生成对象将该对象绑定到第
一变量类型,其中调用指定访问存储器位置以基于第二变量类型执行特定存储器防护操
作,并且该方法还包括:确定第一变量类型是否与第二变量类型匹配,其中响应于确定第一
变量类型与第二变量类型匹配,使得该特定存储器防护操作相对于该存储器位置被执行;
响应于确定第一变量类型不匹配第二变量类型,执行以下操作中的一个或多个:生成错误,
或者使第一变量类型符合第二变量类型,然后使得该特定存储器防护操作相对于该存储器
位置被执行。

30、如条款23-29中的任何一个条款所述的方法,其中生成对象包括执行检查以确
定对位置的访问是否是许可的,并且该方法还包括通过该对象接收多个调用以在不用重复
检查的情况下执行存储器防护操作。

31、如条款23-30中的任何一个条款所述的方法,其中特定存储器防护操作通过除
了向存储器位置发出一条或多条加载或存储指令之外还向存储器位置至少发出由一个或
多个处理器本机支持的一条或多条存储器屏障指令来执行。

32、如条款31所述的方法,其中特定存储器防护操作通过组合由一个或多个处理
器本机支持的一条或多条存储器屏障指令中的两条或更多条存储器屏障指令来执行。

33、如条款23-32中的任何一个条款所述的方法,还包括?#21644;?#36807;该对象接收第二调
用,该第二调用指定与要相对于该存储器位置执行的、不同于该特定存储器防护操作的第
二存储器防护操作;以及使得第二存储器防护操作相对于该存储器位置被执行。

34、如条款23-33中的任何一个条款所述的方法,其中使得特定存储器防护操作被
执行涉及启用与执行一个或多个安全检查的对象相关联的代码,其中该一个或多个安全检
查包括以下的一个或多个:确定传递到调用中的一个或多个?#38382;?#21305;配对象预期的一组参
数、确定对传递到调用中的存储器位置的引用是否为空、或者确定传递到调用中的数组索
引是否?#34892;А?br />

35、如条款23-34中的任何一个条款所述的方法,其中存储器防护操作包括用于一
个或多个访问模式的操作,其中该一个或多个访问模式包括以下的一个或多个:放宽的读
取、放宽的写入、?#36164;?#24615;读取、?#36164;?#24615;写入、原子读取、原?#26377;?#20837;或原子更新。

36、一个或多个非暂态计算机可读介?#21097;?#35813;一个或多个非暂态计算机可读介质存
储指令,当该指令由一个或多个计算设备执行?#20445;?#20351;得如条款1-35中的任何一个条款所述
的方法被执行。

37、一种系统,该系统包括一个或多个计算设备,该一个或多个计算设备包括至少
部分地由计算硬件实现的、被配置为实现如条款1-35中的任何一个条款所述的方法的步骤
的组件。

7.0第二附加公开

下文公开本发明的附加实施例。此外,本节包括从针对Java虚拟机(JVM)编写的各
种规范拉取的附?#29992;?#36848;,在一些实施例中,JVM被用来实?#30452;?#25991;所?#33268;?#30340;技术。然而,不要求
实施例使用本节中?#33268;?#30340;特征中的任何特征或所有特征。

本文描述了用于通过使用支?#25351;?#31181;不同存储器访问模式的“句柄”来提供对存储
器位置的安全?#36879;?#25928;的外来(exotic)访?#23454;?#25216;术。句柄提供了关于对这些存储器位置的操
作的若干级别的抽象。这些抽象中的一个抽象涉及存储器位置?#26087;?#30340;性?#21097;?#22240;为存储器位
置可以存储各种类型的字?#20301;?#20854;它数据结构中的任何类型的字?#20301;?#20854;它数据结构。存储器
位置可以是由语言运行环境(language runtime)(诸如虚拟机或解释器)管理的位置和/或
“不受管理”的位置,因为它们由语言运行环?#25345;?#22806;的存储器管理器(例如,由操作系统分配
给不在语言运行环境内运行的应用的存储器)管理。这些抽象中的另一个抽象涉及句柄支
持的存储器访问模式,因为句柄不将存储器位置锁定到具体的存储器访问模式(如通常在
变量的情况下发生的),而是可以提供由解释器允许并且由底层硬件支持的任何存储器访
问模式。

根据实施例,用于利用这些句柄的方法包括生成被配置为通过不同的存储器防护
操作提供对存储器的安全访?#23454;?#23545;象结构。例如,对象结构可以是(如本文所描述的)
VarHandle类或者被抽象地定义以提供对?#25105;?#31867;型的数据结构的?#25105;?#35775;问模式的任何其它
合?#23454;?#31867;的实例。随后描述存储器防护操作的示例。虽然为了方便起见在本文中句柄被描
述为对象结构,但是将理解的是,取决于编程语言,句柄可以是任何合?#23454;?#26500;造,并?#20063;?#19968;
定需要被严格限制为对象或甚至面向对象的构造。

方法还包括通过对象结构接收指示存储器中的位置并且指定要相对于所指示的
存储器中的位置执行存储器防护操作中的哪一个存储器防护操作的调用(诸如对由它的类
定义的方法的调用)。调用可以使用各种约定来指示位置,诸如通过引用由实?#25351;?#26041;法的语
言运行环境(例如,虚拟机或解释器)管理的位置、通过指定另一对象的先前声明的和/或实
例化的字段、通过引用静态字段、通过引用数组元素或者通过引用不由语言运行环境管理
的位置,诸如尚未分配给语言运行环境的位置或者由不同于语言运行环境或独立于语言运
行环境的过程分配和/或管理的位置(例如,“堆外”位置)。

方法还包括使得指定的存储器防护操作相对于所指示的位置被执行。例如,由对
象结构的类定义和/或以其它方?#25509;?#23545;象结构的类相关联的逻辑可以被执行,以执行存储
器防护操作,和/或向执行硬件发出使得该硬件执行存储器防护操作的?#22270;?#25351;令。

如本文所使用的,存储器防护操作(也被称为存储器屏障、存储器围栏或围栏指
令)是使得中央处理单元(CPU)或编译器对在屏障指令之前和之后发出的存储器操作实施
排序约束的一种类型的屏障指令。存储器屏障可以是必要的,因为许多?#25191;鶦PU采用可能导
致无序执行的性能优化。存储器操作(加载和存储)的这种重新排序通常在单个执行线程内
不被注意,但是除非被仔细控制,否则可以造成并发程序和设备驱动程序中的不?#31245;?#27979;的
行为。顺序约束的确切本质是?#35272;?#30828;件的并且由架构的存储器排序模型定义。一些架构提
供了用于实施不同排序约束的多个屏障。

在实施例中,生成对象结构包括执行检查,以确定对位置和/或与该位置相关联的
数据类型的访问在当前执行上下文内是否是许可的。方法还包括通过对象结构接收多个调
用,以在不重复检查的情况下执行存储器防护操作。

在实施例中,方法由基于例如较?#22270;?#20195;码(诸如Java?#32440;?#30721;或从人类可读?#21019;?#30721;
编译的任何其它代码)中的指令的语言运行环?#25345;?#34892;。方法还包括基于确定由在其上实现
语言运行环境的硬件本机支持的机器存储器架构和/或指令集来确定如何执行指定的存储
器防护操作。方法还包括选择指令集中执行指定的存储器防护操作的具体存储器防护指
令。

在实施例中,方法还包括生成作为类的实例的多个对象结构。方法还包括通过这
多个对象结构接收调用以相对于不同类型的数据结构(例如,不同的原语类型、不同的值类
型等)执行存储器防护操作。调用指定数据结构和/或数据结构的类型。方法还包括基于类
中的逻辑或与类相关联的逻辑来使得指定的存储器防护操作被执行。从而,单个类提供对
各种类型的数据结构的抽象句柄。

在实施例中,方法还包括通过对象结构接收指定要相对于同一存储器位置执行的
不同存储器防护操作的多个调用,不同的存储器防护操作包括具有不同存储器访问模式
(例如,?#36164;?#24615;访问、懒惰的访问、放宽的访问、正常访?#23454;?#30340;任何组合)的至少两个操作。从
而,单个类为同一存储器位置提供多个存储器访问模式。

根据实施例,用于利用所描述的句柄的另一方法(在一些情况下,但不一定在所有
情况下,其构?#19978;?#21069;描述的方法的具体实例)包括识别创建变量句柄对象的第一指令。这样
的指令的一个示例可以是变量声明,该变量声明包括对“查找”方法(诸如类似于本文所描
述的VarHandles.Lookup之类的方法)的调用或者对例如Java?#32440;?#30721;中的这样的方法的等
价启用的调用。第一指令包括至少指示变量句柄对象被配置为提供对其的访?#23454;?#32467;构类型
的配置信息。例如,配置信息可以指定以下各项中的一个或多个的任何合?#23454;?#32452;合:对象类
或数据结构类型、与为对象类或数据结构类型定义的字段对应的字段名称,和/或字段类或
类型。作为另一个示例,配置信息可以引用不受管理的存储器位置,该不受管理的存储器位
置包括尚未分配给正在执行第一指令的语言运行环境(诸如虚拟机)的存储器位置。

方法还包括,响应于第一指令并?#19968;?#20110;配置信息来执行一个或多个检查,以确定
对结构类型的访问是否是许可的。例如,可以执行访问检查,以确定与结构类型相关联的各
种规则或声明(诸如?#20843;接小被頡?#20844;有”声明)是否许可从当前执行上下文(诸如在其中创建变
量句柄对象的函数或方法)内访问结构类型的实例。可以被执行的另一示例检查是确定结
构类型是否实际存在。可选地,方法还包括如果检查中的一个或多个检查失败则抛出错误。
例如,可以抛出指示不能找到被引用的结构类型的错误,或者根据与结构类型相关联的安
全规则对该结构类型的访问将不被许可,诸如如果结构类型是?#25509;?#30340;话。

方法还包括:如果对结构类型的访问是许可的,则创建变量句柄对象。该变量句柄
对象包括被配置为对结构类型的存储器位置执行原子操作的方法。这样的原子操作的示例
可以包括例如存储器防护操作或直接与可以由其上实现语言运行环境的硬件支持的?#22270;?br />硬件指令对应的操作。可选地,操作可以包括由语言运行环境被配置为对结构类型使用的
默认存储器防护模式不支持的至少一个存储器防护操作。例如,存储器访问可以是由字段
如何被声明而暗示的模式。即,如果字段类型被声明为“volatile(?#36164;?#24615;)?#20445;?#21017;在操作的正
常过程期间语言运行环境将仅能够使用?#36164;?#24615;存储器访问模式来执行对该类型的字段进
行读取或写入的指令。相反,变量句柄对象将包括能够访问抽象地存储该字段的存储器位
置的方法,而不限于任何暗示的存储器访问模式。随后将更详细地描述存储器防护操作和
存储器访问模式。

方法还包括识别调用变量句柄对象的方法中的一个方法的第二指令。本文描述这
样的第二指令的示例,诸如“get(获得)”、“setVolatile(?#36164;?#24615;设置)”等?#21462;?#31532;二指令引用
数据结构类型的实例或包括数据结构类型的对象的实例。例如,第二指令可以是包含对象、
数组索引或字段?#26087;?#30340;任何合?#23454;?#32452;合。第二结构可选地包括用于要被执行的对应原子操
作的一个或多个?#38382;?#35832;如用于要读取/写入的存储器位置的数组索引、写入存储器位置的
?#26723;鵲取?#22312;实施例中,第一指令和第二指令可以是同一指令的一部分,或者可以是分开的指
令。

方法还包括识别由所引用的实例指示的存储器位置。例如,可以提供各种查?#19968;?br />制以在存储器中定位被引用的字段,或者定位包括所指示的结构类型的字段的所引用对象
实例的开始位置,以及确定该字段离该开始位置的偏移。可选地,可以执行检查,以确保该
字段具有与创建变量句柄对象时所指示的相同的结构类型。

方法还包括使得对应的原子操作相对于存储器位置被执行。例如,语言运行环境
可以通过确定实现原子操作的具体的?#22270;?#30828;件指令(如果可用的话)并且向硬件发送该指
令来使得对应的原子操作被执行。作为另一个示例,在Java编程语言中,语言运行环境可以
执行“不安全”方法,以直接访问存储器位置。

在实施例中,变量句柄对象的方法包括与以下各项中的一个或多个对应的方法:
放宽的设置、放宽的获得、?#36164;?#24615;获得、?#36164;?#24615;设置、获取获得、释放设置、比较和设置、获得
和设置、原子加法和/或原子减法。

在实施例中,存储器位置是在分配给正在执行第一指令和第二指令的语言运行环
境的存储器的外部的不受管理的存储器位置。在实施例中,存储器位置是受管理的存储器
位置,其可以以各种?#38382;?#26159;静态字段、实例字段、数组、数组元素或其它种类的基于存储器
的变量。

在实施例中,可以提供定义用于访问方法的接口和用于实现方法的逻辑的类类
型。可以为所有变量句柄对象提供单个类类型,而?#36824;?#23427;们对其提供访?#23454;?#25968;据结构的类
型。或者,可以为静态文件和/或实例文件、数组、数组元素、数字原语(primitive)和/或其
它类型的变量提供不同的类类型。

在实施例中,变量句柄对象的方法中的一个或多个方法被定义为具有多态签名,
如在其它节中所描述的。在实施例中,变量句柄对象的方法中的一个或多个方法被定义为
具?#22411;?#29992;签名,并且该方法还包括对于例如编译后的Java?#32440;?#30721;中的变量句柄对象的方法
中的一些或所有方法在调用点处具体化通用签名。

在实施例中,方法还包括识别参?#35760;?#22312;不同的对象或数据结构实例调用变量句柄
对象的不同方法的多条指令,以及响应于该多条指令使得取决于所引用的实例不同的原子
操作相对于同一存储器位置和/或相对于不同的存储器位置被执行。

在实施例中,方法还包括部分地基于该一个或多个检查来确定由变量句柄对象支
持的原子操作。例如,如果数据结构类型已被声明为“final(最终的)?#20445;?#21017;将不支持设置操
作,并且可能(甚至潜在地可能)更新存储器的任何操作将抛出异常。

在其它方面,公开了用于实?#30452;?#25991;所描述的技术的计算设备的系统和计算机可读
介质。

invokedynamic指令

invokedynamic指令简化并且潜在地改进用于JVM上的动态语言的编译器和运行
时系统的实现。invokedynamic指令通过允许语言实现者定义定制的链接行为来做到这一
点。这与其中特定于Java类和接口的链接行为由JVM硬连线的其它JVM指令(诸如
invokevirtual)形成对比。

invokedynamic指令的每个实例被称为动态调用点(dynamic call site)。动态调
用点最初处于未链接状态,这意味着不存在要启用的为调用点指定的方法。如之前提到的,
动态调用点通过引导(bootstrap)方法被链接到方法。动态调用点的引导方法是编译器为
动态类型语言指定的由JVM调用一次以链接?#38236;?#30340;方法。从引导方法返回的对象永久地确
定调用点的行为。

invokedynamic指令包含(以与用于其它启用指令的格式相同的格式的)常量池索
引。该常量池索引引用CONSTANT_InvokeDynamic条目。该条目指定引导方法(CONSTANT_
MethodHandle条目)、动态链接方法的名称,以及对动态链接方法的调用的变元类型和返回
类型。

以下是invokedynamic指令的示例。在这个示例中,运行时系统通过使用引导方法
Example.mybsm来将由该invokedynamic指令指定的动态调用点(其为+,加法运算符)链接
到IntegerOps.adder方法。方法adder和mybsm在上面定义:



注意:这些节中的?#32440;?#30721;示例使用ASM Java?#32440;?#30721;操纵和分析框架的语法。

用invokedynamic指令启用动态链接的方法包括以下步骤:

1.定义引导方法

在运行?#20445;?#24403;JVM首次遇到invokedynamic指令?#20445;?#23427;调用引导方法。该方法将由
invokedynamic指令指定的名称与应当被执行的代码(目标方法)链接,该代码由方法句柄
引用。如果JVM再次执行相同的invokedynamic指令,则它不调用引导方法;它自动调用链接
的方法句柄。

引导方法的返回类型可以是java.lang.invoke.CallSite。CallSite对象表示
invokedynamic指令和它链接到的方法句柄的链接状态。

引导方法采用三个或更多个?#38382;篗ethodHandles.Lookup对象,它是用于在
invokedynamic指令的上下文中创建方法句柄的工厂;String对象,在动态调用点中提到的
方法名;以及MethodType对象,动态调用点的解析的类型签名。

可选地,对invokedynamic指令的一个或多个附加静态变元。从常量池汲取(draw)
的这些变元旨在帮助语言实现者安全、紧凑地将对引导方法有用的附加元数据进行编码。
原则上,名称和确切的变元是冗余的,因为每个调用点可以被给予其?#32422;?#30340;唯一的引导方
法。然而,这样的做法可能产生大的类文件和常量池。对于引导方法的示例,请参见上文。

2.指定常量池条目

如之前所提到的,invokedynamic指令包含对常量池中具有标签CONSTANT_
InvokeDynamic的条目的引用。该条目包含对常量池中其它条目的引用和对属性的引用。本
节简要描述由invokedynamic指令使用的常量池条目。对于更多信息,请参见
java.lang.invoke包文档和Java虚拟机规范(The Java Virtual Machine
Specification)。

示例常量池

以下是来自用于类Example的常量池的摘录(excerpt),其包含将方法+与Java方
法adder链接的引导方法Example.mybsm:



在这个示例中用于invokedynamic指令的常量池条目包含三个值:

CONSTANT_InvokeDynamic tag

Unsigned short of value 0

Constant pool index#234.

值0是指存储在BootstrapMethods属性中的指定符数组中的第一引导方法指定
符。引导方法指定符不在常量池表中;它们包含在这个分开的指定符数组中。每个引导方法
指定符包含对作为引导方法?#26087;?#30340;CONSTANT_MethodHandle常量池条目的索引。

以下是来自同一常量池的摘?#36857;?#20854;示出了BootstrapMethods属性,该属性包含引
导方法指定符的数组:



用于引导方法mybsm方法句柄的常量池条目包含三个值:

CONSTANT_MethodHandle tag

Unsigned byte of value 6

Constant pool index#232.

值6是子标记REF_invokeStatic。

3.使用invokedynamic指令

以下?#32440;?#30721;使用invokedynamic指令来调用引导方法mybsm,该引导方法mybsm将
动态调用点(+,加法运算符)链接到方法adder。这个示例使用+方法将数字40和2相加(为了
清晰,已插入换行符):



前四条指令将整数40和2放在栈上,并且以java.lang.Integer包装器类型将它们
装箱。第五条指令启用动态方法。这条指令参考具有CONSTANT_InvokeDynamic标签的常量
池条目:


这个条目中的CONSTANT_InvokeDynamic标签之后跟着四个?#32440;冢?br />

前两个?#32440;?#24418;成对引用引导方法指定符的CONSTANT_MethodHandle条目的引用:



如之前所提到的,对引导方法指定符的该引用不在常量池表中;它包含在由类文
件属性namedBootstrapMethods定义的分开的数组中。引导方法指定符包含对作为引导方
法?#26087;?#30340;CONSTANT_MethodHandle常量池条目的索引。

在该CONSTANT_MethodHandle常量池条目之后有三个?#32440;冢?#31532;一个?#32440;?#26159;子标签
REF_invokeStatic。这意味着该引导方法将为静态方法创建方法句柄;注意到该引导方法
正在将动态调用点与静态Java方法adder链接。接下来的两个?#32440;?#24418;成表示要为其创建方
法句柄的方法的CONSTANT_Methodref条目:


在这个示例中,引导方法的完全限定名称是Example.mybsm;变元类型为
MethodHandles.Lookup、String和MethodType;而返回类型是CallSite。

接下来的两个?#32440;?#24418;成对CONSTANT_NameAndType条目的引用:



该常量池条目指定方法名(+)、变元类型(两个Integer实例)以及动态调用点的返
回类型(Integer)。

在这个示例中,给出具有箱化的整数值的动态调用点,其与最终目标(adder方法)
的类型完全匹配。实际上,变元和返回类型不需要确?#26800;?#21305;配。例如,invokedynamic指令可
以在JVM栈上传递其操作数中的任一个或两个作为原始整型值。任一个或两个操作数还可
以是未类型化(untyped)的对象值。invokedynamic指令还可以接收其结果作为原始整型值
或未类型化的对象值。在任何情况下,mybsm的dynMethodType变元都将准确地描述
invokedynamic指令所需的方法类型。

独立地,adder方法还可以被赋予原始的或未类型化的变元或返回值。引导方法负
责弥补theDynMethodType和adder方法的类型之间的任何差异。如代码中所示,这容易用对
目标方法的asType调用来完成。

解构MethodHandle

本节使用设置非静态字段的值的具体示例来解构(deconstruct)MethodHandle的
确切启用如何被编译、链接和执行。通过编译和执行基于MethodHandle的jmh微基准来获得
?#32440;?#30721;和内联踪迹的示例。

尽管名称如此,但是Methodhandle可以引用类的底层静态字?#20301;?#23454;例字段。在
OpenJDK实?#31181;校?#22312;执行了?#23454;?#30340;安全检查之后,对这样的句柄的启用产生对
sun.misc.Unsafe上的方法的对应调用。例如,如果字?#38382;?#34987;标记为?#36164;?#24615;的引用类型(非
原语类型),则Unsafe.putObjectVolatile方法将被启用。

如果对MethodHandle的这样的引用被保持在静态最终字段中,则运行环境应当能
够对该引用的启用和当内联发生时它所保持的内容进行常量折叠。在这样的情况下,可能
令人惊讶的是,与对Unsafe或get/putfield?#32440;?#30721;指令直接启用方法相比,生成的机器代
码可以具有竞争力。

通过分别对Unsafe、putObject、putOrderedObject和compareAndSwapObject启用
?#23454;?#30340;方法,扩展MethodHandle实现以支持用于放宽的、懒惰的以及比较和设置原子操作
的句柄是直接的。

给出对类(例如,Receiver)上的非静态字段(例如,具有类型Value的)“vfield”的
写访?#23454;腗ethodHandle可以如下获得:

MethodHandles.Lookup lookup=...

MethodHandler setterOfValueOnReceiver=

lookup.findSetter(Receiver.class,"vfield",Value.class);

然后,该MethodHandle的确切启用将设置Receiver的实例上的字段“vfield”的
值:

Receiver r=...

Value v=...

setterOfValueOnReceiver.invokeExact(r,v);

注意到接收者实例作为第一?#38382;?#34987;传递给invokeExact方法。接收者提供从其访
问它保持的字段“vfield”的值的基本位置(因为Java中直接的l值以及通过字?#20301;?#25968;组元
素的引用的传递没有被支持,所以一对接收者和值是必要的)。

MethodHandle.invokeExact方法被声明为多态签名方法:

public final [email protected] Object invokeExact
(Object...args)throws Throwable;

当javac将启用编译成签名多态方法?#20445;?#23427;使用符号类型描述符作为方法签名,该
符号类型描述符从调用者的实际?#38382;?#21644;返回类型而不是从方法声明的方法签名导出。

invokevirtual指令的示例如?#28388;?#31034;:

13:invokevirtual#12//Methodjava/lang/invoke/MethodHandle.invokeExact:
(Lvarmh/VolatileSet AndGetTest$Receiver;Lvarmh/VolatileSetAndGetTest$Value;)V

注意到方法签名接受两个变元,即类Receiver和类Value的实例,并且返回void,
该签名被称为符号类型描述符。

当句柄被常量折叠?#20445;?#36825;样的启用的内联踪迹如下:


启用包括两个阶段。第一阶段执行高效的运行时安全检查,以确定在调用点处编
码的符号类型描述符是否与MethodHandle的方法类型描述符完全匹配。第二阶段执行写入
访问,在这种情况下是对?#36164;?#24615;字段的写入访问。

在动态生成的类上,MethodHandler.invokeExact启用被内部链接(参见节
“invokeExact启用的链接”)到静态方法invokeExact_MT,该静态方法invokeExact_MT的字
节码是:



用于invocationExact_MT的?#38382;?#22914;下:

MethodHandle实例、setterOfValueOnReceiver;传递给invokeExact方法的?#38382;?br />(r,v);最后是当调用点链接?#22791;?#21152;的调用点的符号类型描述符。

注意到在该时间点处,引用?#38382;?#31867;型被擦除为Object,因此声明该静态方法的类
可以被共享,以用于利用擦除为相同签名的不同方法类型描述符的启用。

该方法首先执行方法类型描述符检查,并且如果该方法类型描述符检查失败则抛
出异常,否则继续是安全的,因为已知调用点的变元类型和返回类型是正确的并且完全匹
配。接下来,利用传递给invokeExact方法的相同?#38382;?#26469;启用MethodHandle实例上的
invokeBasic。

invokeBasic的启用被内部链接到对应于MethodHandle的编译的lambda?#38382;?#30340;动
态生成的类上的静态方法putObjectVolatileFieldCast。MethodHandle的LambdaForm的
vmentry字?#38382;?#34920;征方法putObjectVolatileFieldCast的MemberName,方法
putObjectVolatileFieldCast的?#32440;?#30721;是:



第一个?#38382;?#26159;MethodHandle实例并且后续?#38382;?#26159;传递给invokeExact方法的那些
?#38382;?r,v)。MethodHandle实例是保持要在这个方法结束时与Unsafe.putObjectVolatile
的启用一起使用的字段偏移的直接句柄。在该启用之前:执行安全检查,以确保接收者实例
不为空;并且执行值实例到值(字段)类型的实例的转换检查,以确保运行时编译器具有足
够的信息来执行类型剖析。注意到这不是类型安全所必需的,因为这样的安全检查已经由
invokeExact_MT方法执行?#36824;?#23519;接收者实例的类型不需要转换到接收者类型的实例。

invokeExact启用的链接

MethodHandler.invokeExact的启用经由对返回表征要被链接到的Java方法的
MemberName的Java方法的来自VM的向上调用被内部链接。这种对于VM来说静态已知的被向
上调用的Java方法是MethodHandleNatives.linkMethod:

static MemberName linkMethod(Class<?>callerClass,int refKind,

Class<?>defc,String name,Object type,Object[]appendixResult)

“type”?#38382;?#26159;对应于符号类型描述符的MethodType或String的实例。
“appendixResult”被用来返回可选的额外?#38382;?#35813;?#38382;?#24517;须与由MemberName表征的方法的
最后一个?#38382;?#21305;配,并且将被永久地附加在被链接的调用点处。

在启用invokeExact?#20445;琕M栈已经包含三个值,并且VM将向调用栈添加一个附加参
数,以使得?#38382;?#22914;下:MethodHandle实例,setterOfValueOnReceiver;?#38382;?r,v);最后是作
为“appendixResult”的第一个元素的附加?#38382;?#23427;是调用点的符号类型描述符。

MethodHandle Class

public abstract class MethodHandle

extends Object

方法句柄是具有变元或返回值的可选变换的、对底层方法、构造函数、字?#20301;?#31867;似
?#22270;?#25805;作的类型化的、可直接执行的引用。这些变换是相当通用的,并且包括诸如转换、插
入、?#22659;?#21644;替换之类的模式。

方法句柄内容

方法句柄根据它们的?#38382;?#21644;返回类型被动态地类型化和强类型化。它们不通过其
底层方法的名称或定义类来区分。必须使用匹配方法句柄?#32422;?#30340;类型描述符的符号类型描
述符来启用方法句柄。

每个方法句柄经由type访问器报告其类型描述符。该类型描述符是MethodType对
象,它的结构是一系列类,该一系列类中的一个类是方法的返回类型(如果没有,则是
void.class)。

方法句柄的类型控制它接受的启用的类型,以及应用于它的变换的种类。方法句
柄包含一对名为invokeExact和invoke的特殊启用者(invoker)方法。两个启用者方法都提
供对如通过变元和返回值的变换所修改的、方法句柄的底层方法、构造函数、字?#20301;?#20854;它操
作的直接访问。两个启用者都接受与方法句柄?#32422;?#30340;类型完全匹配的调用。普通的、不确切
的启用者还接受一系列其它调用类型。

方法句柄是不可变的并?#20063;?#20855;有可见状态。当然,它们可以被绑定到展现状态的
底层方法或数据。对于Java存储器模型,任何方法句柄将表现得好像它的(内部)字段中的
所有字段都是最终变量。这意味着对应用可见的任何方法句柄将总是被完全形成。即使方
法句柄通过数据竞争中的共享变量发布,也是如此。

方法句柄不能由用户子类化。实现可以(或者可能不)创建可以经由
Object.getClass操作可见的MethodHandle的内部子类。程序员不应当从方法句柄的具体
类?#36152;?#20851;于该方法句柄的结论,因为方法句柄类层次结构(如果有的话)可以随时间或者跨
来自不同供应商的实现而改变。

方法句柄编译

名为invokeExact或invoke的Java方法调用表达式可以启用来自Java?#21019;?#30721;的方
法句柄。从?#21019;?#30721;的角度来看,这些方法可以采用任何变元,并且它们的结果可以被转换为
任何返回类型。?#38382;?#19978;,这是通过给予启用者方法Object返回类型和可变元数(variable
arity)Object变元来完成的,但是它们具有将这种启用的自由度直接连接到JVM执行栈的
被称为签名多态性的附加特性。

与虚方法通常一样,对invokeExact和invoke的源级调用编译为invokevirtual指
令。较不同寻常的是,编译器必须记录实?#23454;?#21464;元类型,并且可以不对变元执行方法启用转
换。相反,编译器必须根据变元?#32422;?#30340;未转换类型把它们推送到栈上。方法句柄对象?#26087;?#22312;
变元之前被推送到栈上。然后编译器用描述变元和返回类型的符号类型描述符来调用方法
句柄。

为了发出完整的符号类型描述符,编译器还必须确定返回类型。如果存在一个方
法启用表达式的话,则这是基于对方法启用表达式的转换,否则如果启用是表达式则返回
类型为Object,否则如果启用是语句则返回类型为void。转换可以是转换到原语类型(但不
是void)。

作为极端情况,未被转换的null变元被给予java.lang.Void的符号类型描述符。
与Void类型的歧义是无害的,因为除了?#25214;?#29992;,不存在类型为Void的引用。

方法句柄启用

第一次执行invokevirtual指令?#20445;?#23427;通过符号解析指令中的名称并?#24050;?#35777;方法
调用是静态合法的而被链接。对invokeExact和invoke的调用也是如此。在这种情况下,为
了正确语法而检查由编译器发出的符号类型描述符并且解析它所包含的名称。因此,只要
符号类型描述符在语法上良好形成并且类型存在,则启用方法句柄的invokevirtual指令
将总是链接。

当在链接之后执行invokevirtual?#20445;?#39318;先由JVM检查接收方法句柄的类型,以确
保其匹配符号类型描述符。如果类型匹配失败,则意味着调用者启用的方法在被启用的单
独的方法句柄上不存在。

在invokeExact的情况下,启用的类型描述符(在解析符号类型名称之后)必须与
接收方法句柄的方法类型完全匹配。在普通的、不确切的invoke的情况下,解析后的类型描
述符必须是接收者的asType方法的?#34892;?#21464;元。因此,普通的invoke比invokeExact是更被许
可的。在类型匹配之后,对invokeExact的调用直接并且立即启用方法句柄的底层方法(或
其它行为,视情况而定)。

如果由调用者指定的符号类型描述符与方法句柄?#32422;?#30340;类型完全匹配,则对普通
的invoke的调用与对invokeExact的调用的工作方式相同。如果存在类型不匹配,则invoke
尝试调整接收方法句柄的类型(就像通过对asType的调用一样)以获得完全可调用的方法
句柄M2。这允许在调用者和被调用者之间的更强大的方法类型协商。(注意:调整后的方法
句柄M2不是可直接观察的,因此不需要实现来使其具体化)。

启用检查

在典型的程序中,方法句柄类型匹配通常将成功。但是如果匹配失败,则JVM将直
接地(在invokeExact的情况下)或者就像通过对asType的失败的调用一样间接地(在
invoke的情况下)抛出WrongMethodTypeException。

因此,在静态类型化的程序中可能?#20801;?#20026;链接错误的方法类型不匹配可以在使用
方法句柄的程序中?#20801;?#20026;动态的WrongMethodTypeException。

因为方法类型包含“活的”Class对象,所以方法类型匹配考虑类型名称和类加载
器二者。因此,即使方法句柄M在一个类加载器L1中创建并?#20197;?#21478;一个L2中使用,方法句柄
调用也是类型安全的,因为如在L2中解析的调用者的符号类型描述符与如在L1中解析的原
始被调用者方法的符号类型描述符匹配。L1中的解析在M被创建并且其类型被指派时发生,
而L2中的解析在invokevirtual指令被链接时发生。

除了类型描述符的检查之外,方法句柄调用其底层方法的能力也是不受限制的。
如果方法句柄由可访问该方法的类对非公有方法形成,则得到的句柄可以在任何地?#25509;?#25509;
收到对它的引用的任何调用者使用。

与?#30475;?#21551;用反射方法时检查访?#23454;?#26680;心反射(Core Reflection)API不同,当创建
方法句柄时执行方法句柄访问检查。在ldc(参见下文)的情况下,访问检查作为链接在常量
方法句柄底层的常量池条目的一部分来执行。

因此,用于非公有方法的句柄或者用于非公有类中的方法的句柄一般应当保密。
它们不应当被传递给不受信任的代码,除非?#30828;?#21463;信任的代码使用它们是无害的。

方法句柄创建

Java代码可以创建直接访问该代码可访?#23454;?#20219;何方法、构造函数或字段的方法句
柄。这是经由反射的、基于能力的API(被称为MethodHandles.Lookup)来完成的。例如,静态
方法句柄可以从Lookup.findStatic获得。还存在来自核心反射API对象的转换方法,诸如
Lookup.unreflect。

类似于类和?#22336;?#20018;,对应于可访问字段、方法和构造函数的方法句柄还可以在类
文件的常量池中被直接表示为要由ldc?#32440;?#30721;加载的常量。新类型的常量池条目CONSTANT_
MethodHandle直接指向关联的CONSTANT_Methodref、CONSTANT_InterfaceMethodref或
CONSTANT_Fieldref常量池条目。(关于方法句柄常量的详情,请参见Java虚拟机规范的节
4.4.8和节5.4.3.5)。

通过来自具有可变元数修饰符位(0x0080)的方法或构造函数的查?#19968;?#24120;量加载
而产生的方法句柄具有对应的可变元数,就像它们是在asVarargsCollector的帮助下定义
的一样。

方法引用可以参考静态或非静态方法。在非静态的情况下,方法句柄类型包括置
于任何其它变元之前的?#20801;?#25509;收者变元。在方法句柄的类型中,根据最初在其下请求该方
法的类来类型化初始接收者变元。(例如,如果非静态方法句柄是经由ldc获得的,则接收者
的类型是在常量池条目中命名的类)。

方法句柄常量经受与它们对应的?#32440;?#30721;指令相同的链接时间访问检查,并且ldc
指令将抛出对应的链接错误,如果?#32440;?#30721;行为将抛出这样的错误的话。

作为其结果,对受保护?#31245;?#30340;访问仅被限制于访问类或它的子类中的一个子类的
接收者,并且访问类又必须是该受保护?#31245;?#30340;定义类的子类(或包?#20540;?。如果方法引用参
考受保护的非静态方法或者当前包之外的类的字段,则接收者变元将被缩小为访问类的类
型。

当启用虚方法的方法句柄?#20445;?#24635;是在接收者中查找该方法(即,第一个变元)。

还可以创建具体虚方法实现的非虚(non-virtual)方法句柄。这些不基于接收者
类型执行虚拟查找。这样的方法句柄模拟同一方法的invokespecial指令的效果。

使用示例

这是使用的一些示例:

Object x,y;String s;int i;

MethodType mt;MethodHandle mh;

MethodHandles.Lookup lookup=MethodHandles.lookup();

//mt is(char,char)String

mt=MethodType.methodType(String.class,char.class,char.class);

mh=lookup.findVirtual(String.class,"replace",mt);

s=(String)mh.invokeExact("daddy",'d','n');

//invokeExact(Ljava/lang/String;CC)Ljava/lang/String;

assertEquals(s,"nanny");

//weakly typed invocation(using MHs.invoke)

s=(String)mh.invokeWithArguments("sappy",'p','v');

assertEquals(s,"savvy");

//mt is(Object[])List

mt=MethodType.methodType(java.util.List.class,Object[].class);

mh=lookup.findStatic(java.util.Arrays.class,"asList",mt);

assert(mh.isVarargsCollector());

x=mh.invoke("one","two");

//invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;

assertEquals(x,java.util.Arrays.asList("one","two"));

//mt is(Object,Object,Object)Object

mt=MethodType.genericMethodType(3);

mh=mh.asType(mt);

x=mh.invokeExact((Object)1,(Object)2,(Object)3);

//invokeExact(Ljava/lang/Object;Ljava/lang/Object;

Ljava/lang/Object;)Ljava/lang/Object;

assertEquals(x,java.util.Arrays.asList(1,2,3));

//mt is()int

mt=MethodType.methodType(int.class);

mh=lookup.findVirtual(java.util.List.class,"size",mt);

i=(int)mh.invokeExact(java.util.Arrays.asList(1,2,3));

//invokeExact(Ljava/util/List;)I

assert(i==3);

mt=MethodType.methodType(void.class,String.class);

mh=lookup.findVirtual(java.io.PrintStream.class,"println",mt);

mh.invokeExact(System.out,"Hello,world.");

//invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V

对invokeExact或普通的invoke的上面的调用中的每个调用生成具有在以下注释
中指示的符号类型描述符的单个invokevirtual指令。在这些示例中,辅助器方法
assertEquals被假设是对它的变元调用Objects.equals并且声称结果为真的方法。

异常

方法invokeExact和invoke被声明为抛出Throwable(可抛出),这就是说,不存在
对于方法句柄可以抛出什么的静态限制。由于JVM不区分已检查和未检查的异常(当然,除
了通过它们的类区?#31181;?#22806;),因?#31169;?#24050;检查的异常归因于方法句柄启用对?#32440;?#30721;形状没有
特别的影响。但是在Java?#21019;?#30721;中,执行方法句柄调用的方法必须?#20801;?#25243;出Throwable,否
则必须?#38236;?#25429;获所有的Throwable,从而仅重新抛出在上下文中合法的Throwable,并且包
装非法的Throwable。

签名多态性

invokeExact和普通的invoke的不同寻常的编译和链接行为由术语签名多态性引
用。如在Java语言规范中所定义的,签名多态方法是可以与大?#27573;?#30340;调用签名和返回类型
中的任何调用签名和返回类型一起操作的方法。

在?#21019;?#30721;中,?#36824;?#25152;请求的符号类型描述符如何,对签名多态方法的调用将编译。
通常,Java编译器针对命名的方法发出具有给定符号类型描述符的invokevirtual指令。不
同寻常的部分是符号类型描述符从实?#23454;?#21464;元和返回类型派生,而不是从方法声明派生。

当JVM处理包含签名多态调用的?#32440;?#30721;?#20445;?#23427;将成功链接任何这样的调用,而?#36824;?br />其符号类型描述符如何。(为了保持类型安全,JVM将利用合?#23454;?#21160;态类型检查来保卫这样
的调用,如其他地方所描述的)。

要求包括编译器后端的?#32440;?#30721;生成器发出用于这些方法的未变换的符号类型描
述符。要求确定符号链接的工具接受这样的未变换的描述符,而无需报告链接错误。

方法句柄和核心反射API之间的互操作

通过在Lookup API中使用工厂方法,由核心反射API对象表示的任何类?#31245;?#21487;以
被转换为行为上等价的方法句柄。例如,可以使用Lookup.unreflect将反射方法转换为方
法句柄。得到的方法句柄一般提供对底层类?#31245;?#30340;更直接?#36879;?#25928;的访问。

作为特殊情况,当核心反射API被用来查看该类中的签名多态方法invokeExact或
普通的invoke?#20445;?#23427;们看起来就像平常的非多态方法。如由Class.getDeclaredMethod看到
的,它们的反射外观不受该API中的它们的具体状态影响。例如,Method.getModifiers将确
?#26800;?#25253;告对于任何类似声明的方法所需的那些修饰符位,在这种情况下包括native和
varargs位。

与任何反射方法一样,这些方法(当被反射时)可以经由
java.lang.reflect.Method.invoke被启用。然而,这样的反射调用不会导致方法句柄启
用。这样的调用如果被传递了所需的变元(类型Object[]的单个变元),则将忽略该变元并
且将抛出UnsupportedOperationException。

由于invokevirtual指令可以在任何符号类型描述符下本机启用方法句柄,因此
这种反射视?#21152;?#36825;些方法经由?#32440;?#30721;的正常呈现冲突。因此,这两个本机方法在通过
Class.getDeclaredMethod被反射查看时可以仅被视为占位符。

为了获得用于特定类型描述符的启用者方法,使用MethodHandles.exactInvoker
或MethodHandles.invoker。Lookup.findVirtual API还能够为任何指定的类型描述符返
回用于调用invokeExact或普通的invoke的方法句柄。

方法句柄和Java泛型之间的互操作

可以对利用Java泛型类型声明的方法、构造函数或字?#20301;?#24471;方法句柄。与核心反
射API一样,方法句柄的类型将从源级类型的擦除构建。当方法句柄被启用?#20445;?#23427;的变量的
类型或返回值转换类型可以是泛型类型或类型实例。如果这种情况发生,则编译器将在它
构造用于invokevirtual指令的符号类型描述符时用它们的擦除来替换那些类型。

因为类似函数(function-like)的类型和?#38382;?#21270;的Java类型之间存在三方面不匹
配,所以方法句柄不用Java?#38382;?#21270;的(泛型)类型来表示它们的类似函数的类型。

方法类型涉及(range over)从无变元到多达允许的变元的最大数目的所有可能
的元数。泛型不是可变变元的(variadic),因此不能表示这一点。

方法类型可以指定Java泛型类型不能涉及的原语类型的变元。

方法句柄之上的更高阶函数(组合器)常常是跨大?#27573;?#30340;函数类型(包括多个元数
的函数类型)通用的。用Java类型?#38382;?#26469;表示这种通用性是不可能的。

Lookup工厂方法

对Lookup对象的工厂方法与用于方法、构造函数和字段的所有主要用例对应。由
工厂方法创建的每个方法句柄是特定?#32440;?#30721;行为的功能等价物。(?#32440;?#30721;行为在Java虚拟
机规范的节5.4.3.5中描述。)这是这些工厂方法与得到的方法句柄的行为之间的对应关系
的总结:



这里,类型C是正在被搜索?#31245;?#30340;类或接口,其在查找方法中被记录为名为refc的
?#38382;?#26041;法类型MT由返回类型T以及变元类型A*的序列组成。构造函数也具有变元类型A*的
序列并且被认为返回类型C的新创建的对象。MT和字段类型FT二者都被记录为名为type的
?#38382;P问講问齮his表示类型C的自引用;如果它存在,则它总是方法句柄启用的首要变元。
(在一些protected(受保护)?#31245;?#30340;情况下,this可以在类型方面被限制为查找类;参见下
文。)名称arg表示所有其它方法句柄变元。在用于核心反射API的代码示例中,如果所访问
的方法或字?#38382;?#38745;态的,则名称thisOrNull表示?#25214;?#29992;,否则名称thisOrNull表示this。名
称aMethod、aField和aConstructor表示对应于给定?#31245;?#30340;反射对象。

在给定?#31245;?#20855;有可变元数的情况下(即,给定?#31245;?#26159;方法或构造函数),返回的方
法句柄也将具有可变元数。在所有其它情况下,返回的方法句柄将具有固定元数。

?#33268;郟?#26597;找方法句柄与底层类?#31245;?#21644;?#32440;?#30721;行为之间的等价性可以以若干方式被
破坏。如果C不是从查找类的加载器可符号访?#23454;模?#21017;查找仍然可以成功,即使没有等价的
Java表达式或?#32440;?#30721;常量。同样,如果T或MT不是从查找类的加载器可符号可访?#23454;模?#21017;查
找仍然可以成功。例如,?#36824;?#25152;请求的类型如何,对MethodHandle.invokeExact和
MethodHandle.invoke的查找将总是成功。如果安装了安全管理器,则它可以出于各种原因
禁止查找(参见下文)。相比之下,对CONSTANT_MethodHandle常量的ldc指令不经受安全管
理器检查。如果查找方法具有非常大的元数,则由于方法句柄类型具有太多?#38382;?#22240;此方法
句柄创建可能失败。

访问检查

当创建方法句柄?#20445;?#22312;Lookup的工厂方法中应用访问检查。这是与核心反射API的
主要区别,因为java.lang.reflect.Method.invoke在?#30475;?#35843;用时对每个调用者执行访问
检查。

所有访问检查从Lookup对象开始,Lookup对象将其记录的查找类与所有请求进行
比较以创建方法句柄。单个Lookup对象可被用?#21019;?#24314;任?#38382;?#37327;的访问检查过的方法句柄,
所有方法句柄都针对单个查找类进行检查。

Lookup对象可以与其它受信?#26410;?#30721;(诸如元对象协议)共享。共享的Lookup对象委
托对查找类的?#25509;諧稍?#21019;建方法句柄的能力。即使有特权的代码使用Lookup对象,访问检
查也局限为原始查找类的特权。

查找可能失败,因为包含类对于查找类来说不可访问,或者因为期望的类?#31245;?#20002;
失,或者因为期望的类?#31245;?#23545;于查找类来说不可访问,或者因为查找对象不被足够信任以
访问?#31245;薄?#22312;这些情况中的任何情况下,将从尝试的查找?#20449;?#20986;
ReflectiveOperationException。确切的类将是以下之一:NoSuchMethodException–如果
方法被请求但是不存在;NoSuchFieldException–如果字段被请求但是不存在;
IllegalAccessException–如果?#31245;?#23384;在但是访问检查失败。

一般而言,可以为方法M查找方法句柄的条件不比查找类可以编译、验证和解析对
M的调用的条件更严格。在JVM将引发像NoSuchMethodError一样的异常的情况下,方法句柄
查找一般将引发对应的检查异常,诸如NoSuchMethodException。而且启用由查?#20063;?#29983;的方
法句柄的效果完全等价于执行对M的编译后、验证后和解析后的调用。对于字段和构造函数
也是如此。

?#33268;郟?#35775;问检查仅适用于命名和反射方法、构造函数和字段。诸如
MethodHandle.asType之类的其它方法句柄创建方法不要求任何访问检查,并且独立于任
何Lookup对象被使用。

如果期望的?#31245;?#26159;protected(受保护的),则包括查找类必须在与期望?#31245;?#30456;同
的包中或者必须继承该?#31245;?#30340;要求的通常的JVM规则适用。(请参见Java虚拟机规范,节
4.9.2、5.4.3.5和6.4。)另外,如果期望的?#31245;?#26159;不同包中的非静态字?#20301;?#26041;法,则得到的
方法句柄仅适用于查找类或它的子类中的一个子类的对象。这个需求通过将首要的this参
数的类型从C(这将必然是查找类的超类)缩小到查找类?#26087;?#26469;实施。

JVM对invokespecial指令施加类似的要求,即,接收者变元必须匹配解析的方法
和当前类二者。同样,这个要求通过将首要?#38382;?#30340;类型缩小为得到的方法句柄来实施。(请
参见Java虚拟机规范,节4.10.1.9。)

JVM将构造函数和静态初始化器块表示为具有特殊名称的内部方法(“<init>”和
“<clinit>”)。启用指令的内部语法允许它们参考这样的内部方法,就好像它们是正常的方
法一样,但是JVM?#32440;?#30721;验证器拒绝它们。这样的内部方法的查找将产生
NoSuchMethodException。

在一些情况下,嵌套类之间的访问由Java编译器通过创建包装器方法以访问同一
最高级声明中另一个类的?#25509;?#26041;法来获得。例如,嵌套类C.D可以访问其它相关类(诸如,C、
C.D.E或C.B)内的?#25509;諧稍保?#20294;是Java编译器可能需要在那些相关类中生成包装器方法。在
这样的情况下,C.E上的Lookup对象将不能访问那些?#25509;諧稍薄?#23545;这种限制的解决方法是
Lookup.in方法,它可以将对C.E的查找变换为对那些其它类中的任何类的查找,而无需特
殊的特权提升。

对给定查找对象的允许的访问可以根据其lookupMode集合被限制为查找类正常
可访?#23454;某稍?#23376;集。例如,publicLookup方法产生仅允许访问公有类中的公有?#31245;?#30340;查找
对象。调用者敏感的方法lookup产生具有相对于其调用者类的全部能力的查找对象,以模
拟所有支持的?#32440;?#30721;行为。另外,Lookup.in方法可以产生具有比原始查找对象更少的访问
模式的查找对象。

?#25509;?#35775;?#23454;奶致郟?#22914;果查找的查找模式包括访问private(?#25509;??#31245;?#30340;可能性,则
我们称查找具有?#25509;?#35775;问。如在其它地方的相关方法中所记录的,只有具有?#25509;?#35775;?#23454;?#26597;
?#20063;?#25317;有以下能力:

访问查找类的?#25509;?#23383;段、方法和构造函数

创建启用调用者敏感的方法(诸如Class.forName)的方法句柄

创建模拟invokespecial指令的方法句柄

避免对于查找类可访?#23454;?#31867;的包访问检查

创建具有对同一包?#31245;?#20869;的其它类的?#25509;?#35775;?#23454;?#22996;托的查找对象

这些许可中的每个许可是具有?#25509;?#35775;?#23454;?#26597;找对象可以被安全地追溯到起源类
的事实的结果,该起源类的?#32440;?#30721;行为和Java语言访问许可可以由方法句柄可靠地确定和
模拟。

安全管理器交互

虽然?#32440;?#30721;指令仅可以参考相关类加载器中的类,但是这个API可以搜索任何类
中的方法,只要对它的Class对象的引用是可用的。这样的交叉加载器引用对于核心反射
API也是可能的,并且对诸如invokestatic或getfield之类的?#32440;?#30721;指令是不可能的。存在
安全管理器API以允许应用检查这样的交叉加载器引用。这些检查适用于(如在Class上找
到的)MethodHandles.Lookup API和Core Reflection API二者。

如果存在安全管理器,则?#31245;?#26597;找经受附加的检查。对安全管理器进行一至三次
调用。这些调用中的任何调用可以通过抛出SecurityException来拒绝访问。将smgr定义为
安全管理器,将lookc定义为当前查找对象的查找类,将refc定义为在其中查?#39029;稍?#30340;包含
类,以及将defc定义为?#31245;?#22312;其中被实?#35782;?#20041;的类。如果当前查找对象不具有?#25509;?#35775;问,则
值lookc被定义为不存在。调用是根据以下规则进行的:

步骤1:如果lookc不存在,或者如果它的类加载器与refc的类加载器不相同或者
不是refc的类加载器的祖先,则调用smgr.checkPackageAccess(refcPkg),其中refcPkg是
refc的包。

步骤2:如果检索出的?#31245;?#19981;是公有的并且lookc不存在,则调用具有
RuntimePermission(“accessDeclaredMembers”)的smgr.checkPermission。

步骤3:如果检索出的?#31245;?#19981;是公有的,并且如果lookc不存在,并且如果defc和
refc不同,则调用smgr.checkPackageAccess(defcPkg),其中defcPkg是defc的包。

安全检查在其它访问检查已通过之后被执行。因此,上面的规则预先假定?#31245;?#26159;
公有的,否则是正从具有访问该?#31245;?#30340;权限的查找类访?#23454;某稍薄?br />

调用者敏感的方法

少数Java方法具有被称为调用者敏?#34892;?#30340;特殊性质。调用者敏感的方法可以取决
于其直接调用者的身份而表现不同。

如果请求对于调用者敏感的方法的方法句柄,则用于?#32440;?#30721;行为的通用规则适
用,但是它们以特殊方式考虑查找类。得到的方法句柄表现得好像它是从包含在查找类中
的指令被调用的一样,以便调用者敏感的方法检测该查找类。(相比之下,方法句柄的启用
者被忽略。)因此,在调用者敏感的方法的情况下,不同的查找类可能产生表现不同的方法
句柄。

在查找对象是publicLookup()或者不具有?#25509;?#35775;?#23454;?#26576;些其它查找对象的情况
下,查找类被忽略。在这样的情况下,不可以创建调用者敏感的方法句柄,访问被禁止,并且
查找失败并且抛出IllegalAccessException。

?#33268;郟?#20363;如,取决于调用调用者敏感的方法Class.forName(x)的类的类加载器,调
用者敏感的方法Class.forName(x)可以返回不同的类或抛出不同的异常。Class.forName
的公有查找将失败,因为没有合理的方式来确定它的?#32440;?#30721;行为。

如果应用高速缓存方法句柄以用于广泛共享,则它应当使用publicLookup()?#21019;?br />建它们。如果存在Class.forName的查找,则它将失败,并?#20197;?#35813;情况下应用必须采取?#23454;?br />的动作。也许在引导方法的启用期间的稍后的查找可以结合调用者的特殊身份,从而使得
该方法可访问。

函数MethodHandles.lookup是调用者敏感的,以便于可以存在用于查找的安全基
础。JSR 292 API中的?#36127;?#25152;有其它方法?#35272;?#20110;查找对象来检查访问请求。

方法句柄和invokedynamic

抽象地来说,方法句柄仅是类型和符合该类型的某些行为。由于适合面向对象的
系统,因此行为可以包括数据。具体来说,方法句柄可以参考任何JVM方法、字?#20301;?#26500;造函
数,否则它可以是任何先前指定的方法句柄的变换。变换包括部分应用(绑定)、过滤?#36879;?#31181;
?#38382;?#30340;变元混排(shuffling)。

方法句柄的类型被表示为零个或更多个?#38382;?#31867;型的序列,以及可选的返回类型
(或者无类型void)。具体来说,这是MethodType引用,并且可以使用MethodHandle.type从
任何方法句柄提取。

该行为是当方法句柄被启用时使用方法MethodHandle.invokeExact发生的行为。
方法句柄的特殊能力在于invokeExact接受任?#38382;?#37327;的任何类型的变元,并且可以返回任
何类型或void。常规的invokevirtual指令执行该操作。(它被秘密地重写为invokehandle
(如下文所?#33268;?#30340;),但是除了HotSpot实现器之外,这可以被忽略。)

对于方法句柄特有地,invokevirtual指令可以指定任何结构上?#34892;?#30340;类型签名,
并且调用点将链接。在技术上,我们称invokeExact是签名多态的。实?#35782;?#35328;,当链接这样的
调用点?#20445;琂VM可以准备好处理任何类型的签名,这意味着它将必须生成各种各样的适配
器。从用户的角度来看,方法句柄可以包装和/或启用任何类型的任何方法。

具体来说,方法句柄的行为取决于被称为LambdaForm的对象,该对象是逐步操作
的?#22270;?#25551;述。方法句柄的lambda?#38382;?#34987;存储在它的?#38382;?#23383;段中,就好像它的类型被存储在
它的类型字段中一样。

方法句柄的lambda?#38382;?#21487;以完全忽略方法句柄,并且进行与上下文无关的某些操
作,比如抛出异常或返回零。更一般地,它可以查询方法句柄以获得信息。例如,它可以检查
方法句柄的返回类型,并?#20197;?#36820;回某个值之前将它转换为该类型。

更有趣的是,如果方法句柄的类是包含附加数据字段的子类,则lambda?#38382;?#21487;以
在它执行时参考那些字段。

由于方法句柄表达行为多于表达状态,因此它们的字段通常是不可变的。然而,方
法句柄可以容易地被绑定到?#25105;釰ava对象,从而产生闭包(closure)。

“基本类型”系统

为了更简单地实现签名多态性,方法句柄在内部依据基本类型操作。基本类型是
其中许多不方便的区别已被?#23433;?#38500;”的JVM类型,以便于剩余的区别(诸如引用vs.原语和整
型vs.长整型)可以被注意。

对于初学者,在基本类型系?#25345;校?#38500;了浮点型之外的所有32位类型都被擦除成简
单的整型。如果在某个地方需要?#32440;?#20540;,则它可以从完整的整型被遮蔽。因此,仅有四种原
语类型需要担心。

依据基本类型化规则,所有引用类型由java.lang.Object表示。因此,总共有五种
基本类型,这五种基本类型由它们的JVM签名?#22336;?#34920;示为:L、I、J、F、D。我们将用于无类型
void的V添加到这些基本类型.

在Java代码的大部?#31181;校?#23436;整类型系统是生效的。为了命名引用类型,可以查询和
兑现(honor)类加载器和类型约束的系统。从JSR 292运行环境的角度来看,该类型系统是
名称和?#27573;?#30340;复杂组合。在运行环境内部,除了Object和引导(boot)类路径上的其它类型,
使用基本类型没有名称需要担心。

如果在某个地方需要较窄类型的引用,则可以在使用该引用之前发出?#20801;?#30340;检查
转换(checkcast)。事实上,检查转换一般是对Class.cast的调用,其中专用类型是常量
Class引用而不是符号引用名称。

通常,所有额外的转换(诸如整型到?#32440;?#22411;以及Object到命名的引用类型)在优化
器中不出现,优化器对来自上下文的完整类型信息保?#25351;?#36394;。

lambda?#38382;交?#30784;

简而言之,lambda?#38382;?#26159;具有零个或更多个?#38382;講问⒓由?#38646;个或更多个主体表
达式的典型lambda表达式。?#38382;?#21644;表达式值的类型是从基本类型系统汲取的。每个表达式
仅是方法句柄到零个或更多个变元的应用。每个变元或者是常量值或者是先前指定的?#38382;?br />或表达式值。

当lambda?#38382;?#34987;用作方法句柄行为?#20445;?#31532;一个?#38382;?a0)总是方法句柄?#26087;懟?但是
对于lambda?#38382;?#26469;说还存在其它用法。)当方法句柄被启用?#20445;?#22312;任何初始的类型检查之
后,JVM执行方法句柄的lambda?#38382;?#20197;完成方法句柄启用。这导致一些引?#32487;?#25112;,因为
lambda?#38382;?#36890;过评估附加的方法句柄启用来执行。

lambda?#38382;接?#21270;

在lambda?#38382;?#25191;行中还存在允许系统优化自身的一个间接寻址(indirection):
lambda?#38382;?#20855;有被称为vmentry的、(最后)为JVM提供要跳转到的Method*指针的字段,以便
评估lambda?#38382;健?注意:由于Java不能直接表示JVM元数据指针,因此该vmentry实际上具
有类型MemberName,MemberName是用于Method*的?#22270;?#21253;装器。因此毕竟还存在一个间接寻
址来隐藏元数据。)

当首次创建lambda?#38382;絞保?#35813;vmentry指针被初始化为被称为lambda?#38382;?#35299;释器
的方法,该方法可以执行任何lambda?#38382;健?实际上它具有专用于变元的元数和基本类型的
瘦包装器。)lambda?#38382;?#35299;释器非常简单并且慢。在它执行几十次给定的lambda?#38382;?#20043;后,
解释器取来(fetch)或生成用于lambda?#38382;?#30340;?#32440;?#30721;,该?#32440;?#30721;(至少部分地)被定制为
lambda?#38382;?#20027;体。在稳定状态下,所有“热”方法句柄及其“热”lambda?#38382;?#20351;?#32440;?#30721;被生成,
并且使?#32440;?#30721;最终被JIT编译。

因此,在稳定状态下,在没有lambda?#38382;?#35299;释器的情况下,热方法句柄被执行。低
级JVM步骤如下:1.取来MethodHandle.form;2.取来LambdaForm.vmentry;3.取来隐藏的
Method*指针MemberName.vmtarget;4.取来Method::from_compiled_entry;5.跳转?#25509;?#21270;
的代码。如在别处所指出的,如果方法句柄(或者如果lambda?#38382;?#25110;?#31245;?#21517;)是编译时常量,
则可以完成所?#22411;?#24120;的内联。

Invokedynamic

如在JVM中所定义的,invokedynamic包括名称、方法类型签名和引导指定符。调用
者可见的指令行为仅由类型签名来定义,该类型签名确?#26800;?#30830;定哪些类型的变元和返回值
通过栈被混排。当指令首次被执行?#20445;?#30830;定指令的实际行为。与其它启用指令一样,
LinkResolver模块处理在第一次执行时被执行的设置操作。

对于invokedynamic,引导指定符被解析为方法句柄以及零个或更多个额外的常
量变元。(这些都是从常量池汲取的。)名称和签名连同额外的变元和
MethodHandles.Lookup?#38382;?#19968;起被推送到栈上,以具体化请求类,并且引导方法句柄被启
用。(这种对用户指定的方法的诉求可能看起来令人吃惊,但是对JVM来说,它不比为了定位
新类的?#32440;?#30721;而可以执行的ClassLoader(类加载器)操作复杂多少。)

?#24065;?#23548;方法返回?#20445;?#23427;向JVM运行环境给出CallSite对象。这个调用点包含方法句
柄,它(最终)确定链接的invokedynamic指令的确?#34892;?#20026;。由于方法句柄差不多可以做任何
事情,因此invokedynamic指令在链接之后是完全一般的虚拟机指令。

(细心的读者将想知道为什么引导方法不仅仅返回方法句柄。答案是一些调用点
潜在地可以随时间被绑定到一系列不同的方法句柄。这给予Java程序员类似于由JVM用来
管理单态虚拟调用点和多态虚拟调用点的?#22270;?#20195;码打补丁技术。)

invokedynamic实现

因为每个invokedynamic指令(一般)链接到不同的调用点,所以常量池高速缓存
可以包含用于每个invokedynamic指令的分开的条目。(如果其他启用指令使用常量池中的
同一符号引用,则它们可以共享CP高速缓存条目。)CP高速缓存条目(“CPCE”)在被解析时具
有元数据和/或偏移信息的一个词或两个词。

对于invokedynamic,解析后的CPCE包含到提供调用的确?#34892;?#20026;的具体adapter
(适配器)方法的Method*指针。还存在与被称为appendix(附录)的调用点相关联的引用参
数,该引用?#38382;?#34987;存储在用于CPCE的resolved_references数组中。

该方法被称为适配器,因为(一般来说)它混排变元、从调用点提取目标方法句柄,
并且启用该方法句柄。额外的引用?#38382;?#34987;称为附?#36857;?#22240;为它在invokedynamic指令被执行时
附加到变元列表。通常,附录是由引导方法产生的CallSite引用,但是JVM对此?#36824;?#24515;。只要
CPCE中的适配器方法知道如?#26410;?#29702;与CPCE一起存储的附?#36857;?#23601;一切都好。

作为极端情况,如果附录值为空,则它根?#38745;?#34987;推送,并且适配器方法可能不期望
额外的变元。在这种情况下,适配器方法可以是对具有与invokedynamic指令一致的签名的
静态方法的永久链接的引用。这将实际上将invokedynamic变成简单的invokestatic。许多
其它这样的强度?#26723;?strength reduction)优化是可能的。

链接握手

用于invokedynamic CPCE的适配器Method*指针不是由JVM选择,而是由受信任的
Java代码选择。对于附录引用也是如此。事实上,JVM不直接启用引导方法。相反,JVM利用解
析后的引导指定符信息来调用特定于HotSpot的方法
MethodHandleNatives.linkCallSite。(其它JVM实现不一定使用这种握?#24103;?linkCallSite
方法执行JSR 292引导规则所要求的步骤,并且返回两个协调值、适配器方法及其附录。

由于Java不能表示原始Method*指针,因此该方法被包装在被称为MemberName的
?#25509;蠮ava类型中,这类似于用于Klass*的Java镜像。附录是简单的Object引用(或null)。在
经过些许解包之后,这些被插入到CPCE中。

用于invokedynamic的适配器方法

一般而言,适配器方法是由JSR 292运行环境运行中(on the fly)创建的特殊生
成的方法。它从计算当前调用点目标并且启用该目标的lambda?#38382;?#29983;成。lambda?#38382;?#37319;用
与为invokedynamic指令入栈的变元对应的首要?#38382;?#21363;,由该指令的方法签名所需的参
数。lambda?#38382;?#36824;采用尾随的附录变元(如果相关的话)。然后它执行引导方法及其调用点
所需的任何操作。

这是取自实际应用的适配器方法的示例:


这里,invokedynamic指令采用两个变元,double a0和引用a1,并且返回引用t4。
附?#20960;?#38543;在结尾处,在a2中。

Lambda?#38382;?#30340;主体使用子例程Invokers.getCallSiteTarget从附录提取方法句
柄目标。方法句柄被绑定到t3,然后立即对两个首要?#38382;齛0和a1启用。

如通过检查Java代码可以看到的,getCallSiteTarget期望获得非空的CallSite
变元。如果这将失败,则它将意味着受信任的Java代码中有错误(bug),因为受信?#26410;?#30721;负
责向JVM返回一对一致的适配器?#36879;?#24405;。

特殊的非公有例程MethodHandle.invokeBasic是MethodHandle.invokeExact的
未检查版本。它在两个方面与invokeExact不同。第一,它不检查其被调用者具有与调用点
处的类型(完全)匹配的类型。(?#36824;?#24590;样,它可以不抛出WrongMethodTypeException。)

第二,它允许根据在JSR 292运行环?#25345;?#20351;用的基本类型方案来放宽其变元和返
回值的类型化。(参见上文。)

invokedynamic的示例执行序列

invokedynamic指令的调用点的目标可以是任何方法句柄。在最简单的情况下,它
可以是将包含invokedynamic指令的方法连接到某些其它Java语言方法的直接方法句柄。

这是将进行从方法IndyUser.m1到目标方法LibraryCls.m2的这样的连接的事件
和栈帧的序列的示例:



存在两个内?#31354;?#24103;,一个用于绑定到invokedynamic调用点的适配器,一个处理用
于目标方法句柄的启用。

在[关于直接方法句柄的页面]上解释具体的方法internalMemberName和
linkToStatic。

方法句柄启用

在(rewriter.cpp中的)HotSpot的内部,方法句柄启用被重写,以使用被称为
invokehandle的特殊指令。这个指令在许多方面与invokedynamic相似。它解析为适配器方
法指针?#36879;?#24405;。附录(如果不为空)在invoke或invokeExact的?#20801;?#21464;元之后被推送。

解析经由对受信任的Java代码,被称为MethodHandleNatives.linkMethod的方法
的调用来完成。与linkCallSite一样,JVM向linkMethod传递所有解析后的常量池引用,并
接收回一对协调值MemberName和Object。在解包之后,这些作为适配器?#36879;?#24405;被插入到
CPCE中。

相同的自由度适用于invokehandle CPCE条目,就像适用于invokedynamic CPCE
条目一样,并且类似的优化机会适用。与invokedynamic有一个主要区别:如果许多
invokehandle指令都具?#22411;?#19968;的签名和方法名称(“invokeExact”vs.“invoke”),则它们可
以共享单个CPCE条目。

用于invokeExact的适配器方法

MethodHandle.invokeExact的invokevirtual的标?#21152;?#20041;是简单的。用于调用点
的签名(其可以包含任?#25105;?#29992;和原语类型的任?#20301;?#21512;)被解析(在链接?#20445;?#34987;解析一次)为
MethodType。?#30475;?#26041;法句柄被启用?#20445;?#23558;对照被启用的方法句柄的类型来检查方法类型。
(由于方法句柄是计算出的值,所以它当然可以?#30475;?#19981;同,并且类型不一定匹配。)如果两种
类型在任何方面不同,则抛出WrongMethodTypeException。否则,将对给定的类型启用方法
句柄,并返回由调用者期望的类型的值。

用于invokeExact的适配器方法对应地是简单的。它仅仅执行方法类型检查,然后
调用invokeBasic。附录是对进行类型检查所需的resolvedMethodType的引用。

这是示例:


首要变元a0是方法句柄。结尾变元a2是在调用点被解析时计算的方法类型附录。
中间变元a1是方法句柄的唯一变元。

首先,对方法句柄?#36879;?#24405;调用子例程Invokers.checkExactType,从而提取来自方
法句柄的类型并将其与静态调用点类型进行比较。如果没?#20449;?#20986;异常,则控?#21697;?#22238;到适配
器方法。不返回任何值,并且名称t3具有伪类型void。(由于附录表示静态调用点类型,并且
方法句柄的类型是动态可接受的类型,因此这可以被视为简单的动态类型检查。)

接下来,invokeBasic被用来跳转到方法句柄(现在已知它对于该调用是完全安全
的)。简称为t4的结果返回,并且返回到调用者。

用于泛型启用的适配器方法

方法句柄还支持较复杂的启用模式,该较复杂的启用模式可以对单独的变元和返
回值执行类型转换,并且甚至将变元分组成varargs数组。与invokeExact一样,这样的调用
点的解析通过对MethodHandleNatives.linkMethod的调用来实现。在这种情况下,受信任
的Java代码可以返回更灵活?#36879;?#26434;的适配器方法。

这是示例:


如前所述,a0是方法句柄,结尾的a3是方法类型。这里有两个常规变元:引用和整
型。如前所述,第一个任务是检查类型,并且这由Invokers.checkGenericType完成。与较简
单的检查不同,该例程返回值。(如果必要,则该例程还可以抛出
WrongMethodTypeException。)从checkGenericType返回的值实际上是方法句柄t4。该方法
句柄立即对原始变元、?#30001;?#21407;始方法句柄a0、?#30001;?#26399;望的调用点类型a3(不按该顺序)启用。
取决于a0的类型与调用点类型a3之间的匹配或不匹配,t4的实际行为可以非常简单(基本
上是另一个invokeBasic)或非常复杂(具有类型转换的元数改变)。这取决于运行环境。

用于invokeExact的示例执行序列

invokeExact调用的接收者可以是任何种类的方法句柄。在最简单的情况下,它可
以是将包含方法句柄指令的启用者的方法连接到某些其它Java语言方法的直接方法句柄。

这是将进行从方法MHUser.m1到目标方法LibraryCls.m2的这样的连接的事件和
栈帧的序列的示例:



与上面的invokedynamic示例的情况一样,存在两个内?#31354;?#24103;,一个用于绑定到
invokeExact调用点的适配器,一个处理用于目标方法句柄的启用。

关于本文
本文标题:变量句柄.pdf
链接地址:http://www.pqiex.tw/p-6091705.html
关于我们 - 网站声明 - 网?#38236;?#22270; - 资源地图 - 友情链接 - 网站客服 - 联系我们

[email protected] 2017-2018 zhuanlichaxun.net网站版权所有
经营许可证编号:粤ICP备17046363号-1 
 


收起
展开
平码五不中公式规律 2011年1七乐彩走势图 北斗娱乐棋牌官方下载安装 体彩排列五开奖结果 3d今天6码复式 波克棋牌斗地主 重庆百变王牌规则 棋牌十大排行榜大全 拉菲五分彩走势图 组选123的前后 黑龙江十一选五规则