JDK 11是Java SE 11平台版本11的开源参考实现,由JSR 384在Java Community Process中指定。
JDK 11 于2018年9月25日达到一般可用性 .GPL下的生产就绪二进制文件可从Oracle获得 ; 其他供应商的二进制文件很快就会出现。
该版本的功能和时间表是通过JEP流程提出和跟踪的,并由JEP 2.0提案进行了修订 。使用JDK Release Process(JEP 3)生成发布。
2018年6月28日 | Rampdown第一阶段 (从主线分叉) | |
2018年7月26日 | 减速阶段二 | |
2018年8月16日 | 初始发布候选人 | |
2018年8月30日 | 最终候选人 | |
2018年9月25日 | 一般可用性 |
作者 | 约翰罗斯 |
所有者 | 大卫霍姆斯 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 热点/运行时 |
讨论 | valhalla dash dev在openjdk dot java dot net |
功夫 | 中号 |
持续时间 | 中号 |
评论人 | Karen Kinnear,Mikael Vidstedt,Vladimir Kozlov |
受认可 | Mikael Vidstedt |
创建 | 2013/03/19 20:00 |
更新 | 2018/10/26 21:17 |
问题 | 8046171 |
介绍嵌套,这是一种访问控制上下文,与Java编程语言中现有的嵌套类型概念一致。嵌套允许逻辑上属于同一代码实体但被编译为不同类文件的类,以访问彼此的私有成员,而无需编译器插入可访问性扩展桥接方法。
此JEP不关心大规模的访问控制,例如模块。
许多JVM语言支持单个源文件中的多个类(例如Java的嵌套类),或者将非类源工件转换为类文件。然而,从用户的角度来看,这些通常被认为都属于“同一类”,因此用户期望它们共享共同的访问控制机制。为了保持这些期望,编制者经常不得不扩大private
成员的访问权限package
通过添加访问桥:将私有成员的调用编译为目标类中编译器生成的包私有方法的调用,该方法又访问预期的私有成员。这些桥接器破坏了封装,略微增加了已部署应用程序的大小,并且可能会混淆用户和工具。形成嵌套的一组类文件的正式概念,其中嵌套配合共享公共访问控制机制,允许以更简单,更安全,更透明的方式直接实现期望的结果。
公共访问控制上下文的概念也出现在其他地方,例如主机类机制Unsafe.defineAnonymousClass()
,其中动态加载的类可以使用主机的访问控制上下文。一个正式的嵌套成员概念会使这个机制更加稳固(但实际上提供一个支持的替代方案defineAnonymousClass()
将是一个单独的努力。)
Java语言规范允许类和接口彼此嵌套。在顶级声明(JLS 7.6)的范围内,任何数量的类型都可以嵌套。这些嵌套类型具有相互无限制的访问权限(JLS 6.6.1),包括私有字段,方法和构造函数。我们可以描述一个顶级类型,以及嵌套在其中的所有类型,形成一个嵌套,并且嵌套的两个成员被描述为嵌套。
私有访问在包含顶级类型的整个声明中是完整的(无差别的,扁平的)。(人们可以将其视为定义一种“迷你包”的顶级类型,在其中授予额外访问权限,甚至超出提供给同一Java包的其他成员的权限。)
今天,JVM访问规则不允许在嵌套之间进行私有访问。为了提供允许的访问,Java源代码编译器必须引入一个间接级别。例如,私有成员的调用被编译为目标类中编译器生成的package-private,bridging方法的调用,该方法又调用预期的私有方法。仅在需要时生成这些访问桥以满足嵌套内请求的成员访问。
缺乏JVM支持嵌套内私有访问的另一个后果是,核心反射也拒绝访问。java.lang.reflect.Method.invoke
从一个嵌套到另一个嵌套的反射方法调用(使用)抛出IllegalAccessError
(除非禁用访问控制)。考虑到反射调用应该与源级调用相同,这是令人惊讶的。类似地,MethodHandle
API拒绝直接“查找”私有嵌套方法,但提供特殊支持Lookup.in
以允许表达源级别调用语义。
通过编写嵌套的概念和JVM中相关的访问规则,我们简化了Java源代码编译器的工作,加强了现有的访问检查,并从核心反射和MethodHandle
API中删除了令人惊讶的行为。我们还允许未来的增强功能,以利用“巢”概念。例如:
Unsafe.defineAnonymousClass()
API 替代品可以将新类创建为现有类的嵌套。可在此处找到当前提出的JVMS更改集。
现有的类文件格式定义了InnerClasses
和EnclosingMethod
属性(JVMS 4.7.6和4.7.7),以允许Java源代码编译器(例如javac
)来实现源级别嵌套关系。每个嵌套类型都编译为自己的类文件,不同的类文件由这些属性的值“链接”。虽然这些属性足以让JVM确定嵌套,但它们并不直接适用于访问控制,并且本质上与单个Java语言概念相关联。
为了允许更广泛,更一般的巢式概念超越简单的Java语言嵌套类型,并且为了有效的访问控制检查,建议修改类文件格式以定义两个新属性。一个嵌套成员(通常是顶级类)被指定为嵌套主机,并包含一个attribute(NestMembers
)来标识其他静态已知的嵌套成员。每个其他嵌套成员都有一个attribute(NestHost
)来标识其嵌套主机。
我们将通过向JVMS 5.4.4添加类似以下子句的内容来调整JVM的访问规则:
当且仅当满足以下任一条件时,字或方法R才可被类或接口D访问 :
- ...
- R是私有的,在不同的类或接口C中声明,而C和D是同伴。
对于类型C和D是嵌套,它们必须具有相同的嵌套主机。如果类型C在其属性中列出D,则它声称是由D托管的嵌套的成员。如果D还在其属性中列出C,则验证成员资格。D隐含地是它承载的嵌套的成员。NestHost
NestMembers
具有no NestHost
或NestMembers
attribute的类隐式地形成一个嵌套,其自身作为嵌套主机,以及唯一的嵌套成员。
松散的访问规则将影响以下活动期间的访问检查:
java.lang.reflect.AccessibleObject
java.lang.invoke.MethodHandles.Lookup
通过更改访问规则,并对字节代码规则进行适当调整,我们可以允许生成调用字节码的简化规则:
invokespecial
对于私人嵌套构造函数,invokevirtual
对于私有非接口,nestmate实例方法,invokeinterface
用于私有接口,nestmate实例方法; 和invokestatic
对于私人的巢穴,静态方法这放宽了必须使用invokespecial
(JVMS 6.5)调用私有接口方法的现有约束,并且更通常允许invokevirtual
用于私有方法调用,而不是添加到周围的复杂使用规则invokespecial
。可以对MethodHandle
调用的语义进行类似的更改(这反映了调用字节代码约束)。
必须先验证Nest成员身份,然后才能继续进行依赖于nestmate访问的访问检查。这可能发生在成员访问的时间,或者早在类的验证时间或中间的某个地方,例如方法的JIT编译。如果尚未加载嵌套成员资格验证,则需要加载嵌套成员资格验证。为避免可能不必要的类加载,应尽可能晚地(即访问检查时)执行嵌套成员资格验证。如果依赖于嵌套访问,则通过要求存在嵌套主机类来减轻所引入的不兼容性的影响。
为了保持嵌套的完整性,建议至少在开始时,禁止使用任何形式的类转换或类重新定义来修改嵌套类文件属性。
在我们引入新的类文件属性时,习惯上提供一种使用核心反射检查/查询这些属性的方法。这是目前设想三种方法java.lang.Class
:getNestHost
,getNestMembers
,和isNestmateOf
。
建议的更改虽然在概念上很简单,但会影响所有明确或隐含地涉及访问控制或与方法调用模式相关的规范和API。这些包括:
Method
调用规则Field
访问规则MethodHandle
查找规则java.lang.instrument
API,JDWP和JDI(com.sun.jdi.VirtualMachine
)
建议的更改简化了将Java源代码构造映射到类文件的规则,因此对选择使用它们的Java源代码编译器有很多影响:
该javac
编译器将生成更新最新版本的类文件时要充分利用nestmates。(现在使用访问桥等生成旧版本)
对类文件进行操作或生成或处理字节码的任何工具都可能受到这些更改的影响。这些工具至少必须容忍新类文件属性的存在,并允许更改字节码规则。例如:
javap
类文件检测工具,访问检查的额外复杂性是必须要检查的。特别是围绕nest主机类的解析和可能出现的错误的问题。我们已经遇到并解决了编译器线程需要加载嵌套主机类的问题 - 这在编译器线程中是不允许的。我们需要确保实施能够处理这些条件,并确保规范不受其引入的影响。
我们可以根据需要继续在Java编译器中生成桥接方法。这是一个难以预测的过程。例如,Project Lambda在存在内部类时难以解析方法句柄常量,从而导致一种新类型的桥接方法。由于编译器生成的桥接方法很棘手且难以预测,因此它们也很麻烦,很难通过各种工具进行分析,包括反编译器和调试器。
最初的提案考虑使用现有InnerClasses
和 EnclosingMethod
属性来建立nestmate-ship。但是引入特定的nestmate相关属性都会使得巢友比仅与语言级嵌套类型相关更通用,并且允许更有效的实现。此外,如果我们选择了急切的嵌套成员资格验证检查,它将改变现有属性的语义,这可能是兼容性问题。虽然javac
编译器可能会保持“内部类”和“嵌套成员”属性对齐,但这是编译器的选择,JVM将完全独立地处理它们。
在讨论当前方法之前,讨论了如何通过classfile属性最好地表达嵌套关系。对于非集中式方法,一个建议是每个嵌套由UUID识别。该讨论的结论如下:
这样的提议有两个部分:
基于UUID的嵌套新命名约定。这是JVM中的一个新概念,需要新的基础架构来管理(生成,转码,验证,反映,调试)。这意味着新的错误和新的攻击面。在没有决定性好处的情况下,最好重用现有的名称空间,并且(特别是)JVM的类型名称字典。
单向链接。UUID是一个没有内容的纯身份,不包含其嵌套成员的列表。嵌套成员指向嵌套(通过UUID)。只需提及适当的UUID,任何类都可以将自身注入嵌套(在同一个包中)。单向链接意味着无法枚举嵌套。这使一些优化变得复杂(基于密封类型)。巢的安全性和密封性降低到包装的安全性和密封性。PRIVATE只是默认范围访问控制的别名。
对不起,但与目前的提案相比,这对我来说都没有吸引力。
我们需要一组广泛的JVM测试来验证新的访问规则并调整字节代码语义以支持同伴。
类似地,我们还需要针对核心反射,方法句柄,var-handle和外部访问API(如JDWP,JVM TI和JNI)的其他测试。
由于此处未提出语言更改,因此不需要新的语言合规性测试。
在javac
修改编译器以利用nestmate访问之后,对语言符合性测试自然会出现对巢友的充分功能测试。
新规则必须与新的类文件版本号相关联,因为我们需要确保Java源编译器仅在定位了解它们的JVM时生成依赖于新属性和规则的字节码。这样做的必然结果是,如果JVM出现在具有合适版本号的类文件中,它将仅识别新属性并对其进行操作。新的类文件版本给更广泛的Java生态系统中的工具带来了负担,但我们不希望Nestmates成为唯一的技术,在目标JDK版本中,它将依赖于新的类文件版本号。
放松访问几乎没有一致性风险。今天编译和运行的所有Java语言访问都将使用nestmate更改进行编译和运行,而不会更改源代码。禁用访问检查(via setAccessible
)以反复访问nestmates的代码将继续使用nestmate更改正确运行 - 但可以更新为不禁用访问检查。
在某些情况下,检查禁止行为的合规性测试可能会失败。例如:
invokeinterface
无法用于专用接口方法的测试将失败,因为可以使用这些更改。用户兼容性几乎没有风险,因为提案放宽了访问权限。但是,如果用户已“发现”并利用了访问桥接方法,则在删除网桥后,他们将无法执行此操作。这种风险非常小,因为桥接方法首先没有稳定的名称。
系统完整性几乎没有或没有风险,因为提议的规则仅在单个运行时包内授予新访问权限。通过消除对桥接方法的需求,将系统地降低不同顶级类之间的潜在访问。
嵌套成员资格验证需要存在nest-host类,即使该类本身未使用(除了作为嵌套成员的容器)。这可能会在以下三个方面产生影响:
类加载的顺序可能会更改,因为访问检查可能需要嵌套主机,而不是直接使用嵌套主机时。这不是一个问题,因为类只加载,而不是初始化,并且对类加载顺序的依赖性(与类初始化顺序不同)非常罕见。
这可能会影响从分布式表单中删除未使用的类的测试/应用程序,并且未使用嵌套主机。通过将嵌套成员资格验证留到需要进行嵌套访问检查的时间,我们的目标是最小化此问题的影响,但在某些情况下,最终用户将不得不改变他们分发代码的方式。我们认为这是一个非常小的风险,因为将顶级类纯粹用作无状态容器并不常见,它只包含静态嵌套类型,其中嵌套类型将依赖于彼此的私有访问。
嵌套主机的解析还将类加载(以及相关异常的可能性)引入JVM的访问检查逻辑。这主要是JVM实现者关注的问题。必须注意确保所有可能导致VM访问检查的路径或者排除加载嵌套主机的可能性,否则可以应对它。同样可能发生的潜在异常。从用户角度来看,由于Java代码很少对类加载的时间和位置进行假设,因此风险非常小,只有存在格式错误的类文件才会出现异常。
作者 | Brian Goetz |
所有者 | 路易斯福尔坦 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 热点/运行时 |
讨论 | 在openjdk dot java dot net的琥珀色破折号开发 |
功夫 | 中号 |
持续时间 | 中号 |
涉及到 | JEP 303:LDC和INVOKEDYNAMIC指令的内在函数 |
评论人 | 马克莱因霍尔德 |
受认可 | 马克莱因霍尔德 |
创建 | 2017/03/20 20:26 |
更新 | 2018/09/10 18:57 |
问题 | 8177279 |
扩展Java类文件格式以支持新的常量池形式 CONSTANT_Dynamic
。CONSTANT_Dynamic
将委托创建加载到引导方法,就像链接invokedynamic
调用站点将链接委托给引导方法一样。
我们寻求降低创建新形式的可实现类文件常量的成本和中断,这反过来又为语言设计者和编译器实现者提供了更广泛的表达性和性能选择。我们通过创建一个新的常量池形式来实现这一点,该形式可以使用用户提供的行为进行参数化,采用带有静态参数的bootstrap方法。
我们还将调整JVM和引导程序方法之间的链接时握手,以便使所使用的引导程序APIinvokedynamic
适用于动态常量。
根据invokedynamic
我们的经验,我们将调整两者invokedynamic
和动态常量的自举握手,放松对参数列表处理到引导方法的某些限制。
这项工作需要JDK库的一些原型设计支持几种常量类型的代表性样本,特别是变量句柄(JEP 193)。为了支持这种原型设计,这项工作将与其他关于常量表达式的基本语言支持的工作相协调(JEP 303)。
此JEP旨在支持常量池中的任意常量。虽然有关于引导方法的其他用途的建议,例如方法配方,但是这个JEP专注于一种用途。
此JEP的成功不依赖于Java语言或Java编译器后端的支持,但如果编译器后端使用它,则更有可能成功。
尽管大的聚合常量是Java翻译策略中的一个弱点,但是在有更好的方法将它们封装成常量形式(例如冻结数组或原始专用列表)之前,这个JEP无法解决聚合问题。
作为一个最低要求,暴露常量池形式来描述基本类镜像(int.class
)null
,enum
常量和大多数形式都应该是实用VarHandle
的CONSTANT_Dynamic
。
动态常量必须可用于当前允许常规常量池常量的任何上下文中,例如CONSTANT_String
和CONSTANT_MethodType
。因此,它们必须是ldc
指令的有效操作数,并且必须被允许作为引导方法的静态参数。
bootstrap-method握手应该支持包含数千个组件参数的复杂常量,从而提升了251个常量参数的当前限制。作为一个伸展目标,还应该有一种方法让引导方法更准确地控制通过解析引导方法参数产生的链接错误。
在工作结束时,我们还应该有理由相信这种机制可以用于各种各样的库类型,例如派生方法句柄,小型不可变集合(列表,映射,集合),数字,正则表达式,字符串格式化程序或简单数据类。
应确定并记录后续工作。请参阅下面的“可能的扩展”。
Java虚拟机规范的4.4节描述了常量池的格式。添加新的常量池形式(例如Java 7中的支持MethodHandle
和MethodType
引入)是一项重大的工作,并且会在生态系统中发出涟漪,因为它会影响解析或解释类文件的所有代码。这为创建新的恒定池形式提供了很高的标准。
使用invokedynamic
,将常量池中存储复杂数据的值相乘,因为invokedynamic
引导程序的静态参数列表 是一系列常量。invokedynamic
协议的设计者 (例如LambdaMetafactory
Java 8中添加的)通常很难满足根据现有常量集编码行为的需要 - 这反过来又需要在引导程序本身中具有额外的容易出错的验证和提取逻辑。更丰富,更灵活,更高类型的常量消除了invokedynamic
协议开发的摩擦,这反过来又促进了复杂逻辑从运行时到链接时的移动,提高了程序性能并简化了编译器逻辑。
正如invokedynamic
调用站点的链接涉及从JVM到基于Java的链接逻辑的上行调用一样,我们可以将同样的技巧应用于常量池条目的解析。甲CONSTANT_Dynamic
恒定池条目编码以执行分辨率自举方法(A MethodHandle
),所述常数(的类型Class
),以及任何静态引导参数(常数的任意序列,在动态常数之间的常量池限制周期。)
我们添加了一个新的常量池形式,CONSTANT_Dynamic
(新的常量标记17),它的标记字节后面有两个组件:bootstrap方法的索引,格式与a中的索引相同 CONSTANT_InvokeDynamic
,a CONSTANT_NameAndType
,编码预期类型。
在行为上,CONSTANT_Dynamic
通过对以下参数执行其引导方法来解析常量:1。本地Lookup
对象,2。String
表示常量的名称组件,3。Class
表示期望的常量类型,以及4.任何剩余的引导参数。与此同时invokedynamic
,多个线程可以竞争解决,但将选择一个独特的赢家,并丢弃任何其他竞争的答案。而不是CallSite
像invokedynamic
指令所要求的那样返回一个对象,bootstrap方法将返回一个值,该值将立即转换为所需的类型。
与此同时invokedynamic
,除了类型之外,name组件是一个附加通道,用于将表达式信息传递给bootstrap方法。预期正如invokedynamic
指令查找名称组件的用途(例如,方法名称或某些特殊描述符)一样,动态常量也将找到名称的用途(例如,enum
常量的名称或符号常量的拼写) )。把CONSTANT_NameAndType
在两地使得一个更经常的设计。在效果上,CONSTANT_Methodref
和 CONSTANT_Fieldref
常数用来指的类名称的成员,而类似的CONSTANT_InvokeDynamic
和 CONSTANT_Dynamic
常数用于指命名实体与用户编程的引导程序。
具有两个invokedynamic
和 的常量的类型组件CONSTANT_Dynamic
确定调用站点的有效类型或常量(分别)。引导方法不会贡献或约束此类型信息,因此引导方法可能(通常是)弱类型,而字节码本身始终是强类型的。
为了放宽对引导说明符的长度限制,将调整定义引导方法调用的语言(具有完全向后兼容性),以允许变量arity(ACC_VARARGS
)引导方法将所有剩余的静态参数吸收到其尾随参数中,即使存在是2 ^ 16-1。(类文件格式已经允许这样,但是没有办法读取过长的引导参数列表。)为了保持一致性,如果目标方法具有可变的arity ,那么 invokeWithArguments
方法MethodHandle
也将以这种方式扩展。这样的引导方法调用可以在弱类型的方法来指定invokeWithArguments
和invoke
,就像今天它在来指定invoke
一个人。
控制引导链接错误已被证明是来自用户的错误和RFE的反复出现的来源,并且invokedynamic
随着引导方法变得更加复杂(因为它们必须具有动态常量),趋势可能会加速。如果我们能找到一种方法来提供对引导方法异常的更全面控制,并且可以简单地完成,我们将考虑将其作为此JEP的一部分提供。否则,它将列入未来的增强功能列表。
Java虚拟机规范草案CONSTANT_Dynamic
可以在JDK-8189199中找到,这是与此JEP的主要开发问题相关的CSR问题。
未来可能的扩展包括:
ConstantValue
静态字段的属性有关设计选择的讨论可以在JDK-8161256中找到 ,它涉及许多相关的RFE。目前的JEP是从这个更大的特征列表中提炼出来的。
许多用途CONSTANT_Dynamic
可以用等效的invokedynamic
调用代替 。(调用将采用零参数并绑定到返回所需常量的方法句柄。)但是,这样的解决方法对于关键要求没有帮助,但是,它能够将合成常量作为引导参数传递。
另一种替代方法CONSTANT_Dynamic
是使用static final
字段命名所需的常量,并在静态初始化器(
)中计算它们的值。这种方法需要额外的元数据(每个常量的一次性字段定义)并且不足以避免引导循环问题。这些问题通过使用解耦的静态初始化器构建私有嵌套类来解决,但这也需要额外的元数据。如果语言演变为使用许多这样的常量,那么来自过多元数据的应用程序就会膨胀。
另一种方法是旋转静态方法,这些方法执行常量精化逻辑,然后懒惰地调用它们invokedynamic
。同样,这种一次性方法是元数据开销,与之相比较大CONSTANT_Dynamic
。
实际上,用于模拟这些功能的元数据开销太大。
此功能以JVM为中心,因此不依赖于更高的软件层。
为了确保正确的设计,它至少需要通过几个用例进行实验性采用。即使原型被丢弃,库原型也是必须的。
与此同时invokedynamic
,广泛采用需要javac
后端使用,这反过来可能需要语言扩展。作为基本的第一步int.class
,如果可能的话,应该检查需要隐藏静态方法的转换变通方法,例如转换或切换映射表,并用新常量重新构造。
所有者 | Dmitrij Pochepko |
类型 | 特征 |
范围 | 履行 |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 热点/编译器 |
讨论 | hotspot dash编译器开发人员在openjdk dot java dot net |
功夫 | 大号 |
持续时间 | 大号 |
评论人 | Mikael Vidstedt,Vladimir Kozlov |
受认可 | 弗拉基米尔科兹洛夫 |
创建 | 2017/10/10 12:40 |
更新 | 2018/09/10 14:45 |
问题 | 8189104 |
改进现有的字符串和数组内在函数,并java.lang.Math
在AArch64处理器上实现sin,cos和log函数的新内在函数。
专用的CPU架构特定的代码模式可提高用户应用程序和基准测试的性能。
内部函数用于利用CPU体系结构特定的汇编代码,而代码是针对给定方法执行的通用Java代码,以提高性能。虽然大多数内在函数已经在AArch64端口中实现,java.lang.Math
但仍然缺少以下方法的优化内在函数:
该JEP旨在通过为这些方法实施优化的内在函数来弥补这一差距。
同时,虽然大多数内在函数已经在AArch64端口中实现,但是某些内在函数的当前实现可能不是最佳的。具体而言,AArch64架构的一些内在函数可能受益于软件预取指令,存储器地址对齐,多流水线CPU的指令放置,以及用更快的指令或SIMD指令替换某些指令模式。
这包括(但不限于)这样的典型操作String::compareTo
,String::indexOf
,StringCoding::hasNegatives
,Arrays::equals
,StringUTF16::compress
,StringLatin1::inflate
,和各种校验和计算。
根据内在算法,最常见的内部用例和CPU细节,可以考虑以下更改:
UseSIMDForMemoryOps
现有算法具有非NEON版本,则此类代码(如果将创建任何代码)将被置于标志(例如)下。jtreg
测试套件测试功能正确性。如果现有测试库未提供足够的覆盖范围,则可能会创建其他测试。
所有者 | Aleksey Shipilev |
类型 | 特征 |
范围 | 履行 |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 热点/ gc |
讨论 | hotspot dash gc dash dev at openjdk dot java dot net |
功夫 | 小号 |
持续时间 | 小号 |
涉及到 | JEP 304:垃圾收集器接口 |
评论人 | Andrew Haley,罗曼肯克 |
受认可 | Mikael Vidstedt |
创建 | 2017/02/14 08:23 |
更新 | 2018/09/24 15:53 |
问题 | 8174901 |
开发一个处理内存分配但不实现任何实际内存回收机制的GC。一旦可用的Java堆耗尽,JVM将关闭。
提供完全被动的GC实现,具有有限的分配限制和尽可能低的延迟开销,但代价是内存占用和内存吞吐量。成功的实现是孤立的代码更改,不会触及其他GC,并且在JVM的其余部分中进行最小的更改。
将手动内存管理功能引入Java语言和/或JVM并不是一个目标。引入新API来管理Java堆不是目标。更改或清理内部JVM接口以适应此GC不是目标。
众所周知,Java实现可广泛选择高度可配置的GC实现。各种可用的收集器最终满足不同的需求,即使它们的可配置性使它们的功能相交。有时更容易维护单独的实现,而不是在现有GC实现上堆积另一个配置选项。
有一些用例,其中一个简单的无操作GC证明是有用的:
性能测试。拥有几乎不做任何事情的GC是一个有用的工具,可以为其他真正的GC进行差异性能分析。使用无操作GC可以帮助过滤掉GC引起的性能假象,例如GC工作人员调度,GC障碍成本,不幸时间触发的GC循环,位置变化等。此外,还存在非GC引发的延迟伪像(例如,调度打嗝,编译器转换打嗝等),并删除GC引起的伪像有助于对比这些。例如,使用no-op GC可以估算低延迟GC工作的自然“背景”延迟基线。
记忆压力测试。对于Java代码测试,为分配内存建立阈值的方法对于断言内存压力不变量很有用。今天,我们必须从MXBeans中获取分配数据,甚至使用解析GC日志。使GC只接受有限数量的分配,并在堆耗尽时失败,简化了测试。例如,知道测试应该分配不超过1 GB的内存,我们可以使用-Xmx1g配置no-op GC,如果违反了该约束,则让它与堆转储一起崩溃。
VM接口测试。对于VM开发目的,使用简单的GC有助于理解VM-GC接口具有功能分配器所需的绝对最低要求。对于无操作GC,接口不应该有任何实现,良好的接口意味着Epsilon的BarrierSet只使用默认实现中的无操作屏障实现。这可以证明VM-GC接口是理智的,这对于代替JEP 304(“垃圾收集器接口”)很重要。
非常短暂的工作。短期工作可能依赖于快速退出以释放资源(例如堆内存)。在这种情况下,接受GC循环以徒劳地清理堆是浪费时间,因为无论如何堆都将被释放。请注意,GC周期可能需要一段时间,因为它取决于堆中的实时数据量,这可能很多。
最后一次延迟改进。对于超级延迟敏感的应用程序,开发人员可以清楚地了解内存分配并准确了解应用程序内存占用,甚至拥有(几乎)完全无垃圾的应用程序,接受GC循环可能是一个设计问题。还有一些情况下,重新启动JVM - 让负载均衡器找出故障转移 - 有时是比接受GC周期更好的恢复策略。在那些应用中,长GC循环可能被认为是错误的,因为这会延长故障的检测,并最终延迟恢复。
最后一次吞吐量改进。即使对于非分配工作负载,GC的选择意味着选择工作负载必须使用的GC障碍集,即使实际上没有GC循环也是如此。所有OpenJDK GC都是世代的(非主线Shenandoah和ZGC除外),它们至少发出一个参考写屏障。避免这种障碍可以带来最后一点的吞吐量改进。对此有一些地方警告,见下文。
Epsilon GC的外观和感觉与其他任何OpenJDK GC一样-XX:+UseEpsilonGC
。
Epsilon GC通过在单个连续的已分配内存块中实现线性分配来工作。这允许GC中的简单无锁TLAB(线程局部分配缓冲区)发布代码,然后可以重用现有VM代码处理的无锁内TLAB分配。发布TLAB还有助于保持由实际分配的进程限制的进程占用驻留内存。Humone / out-of-TLAB分配由相同的代码处理,因为在此方案中分配TLAB和分配大对象之间几乎没有区别。
Epsilon使用的屏障集是完全空的/无操作,因为GC不执行任何GC循环,因此不关心对象图,对象标记,对象复制等。引入新的屏障集实现是可能是此实现中最具破坏性的JVM更改。
由于Epsilon运行时接口的唯一重要部分是发布TLAB,因此其延迟在很大程度上取决于发布的TLAB大小。对于任意大的TLAB和任意大的堆,延迟开销可以用任意低的正值来描述,因此名称。(替代原始故事:“epsilon”经常表示“空符号”,与本GC的无操作性质一致)。
一旦Java堆耗尽,就不可能进行分配,也不可能进行内存回收,因此我们必须失败。那时有几种选择; 大多数都符合现有的GC所做的:
OutOfMemoryError
一个描述性的消息。-XX:+HeapDumpOnOutOfMemoryError
)-XX:OnOutOfMemoryError=...
),例如,启动调试器或通知外部监控系统有关故障。在System.gc()
调用时没有任何事情要做,因为没有实现内存回收代码。实施可能会警告用户强制GC的尝试是徒劳的。
原型运行通过在小型工作负载中存活并在较大工作负载上可预测地失败来证明该概念。可以在沙箱存储库中找到原型实现和一些测试:
$ hg clone http://hg.openjdk.java.net/jdk/sandbox sandbox $ hg up -r epsilon-gc-branch $ sh ./configure $ make images
通过使用以下方法可以看到基线和修补的运行时之间的差异:
$ hg diff -r default:epsilon-gc-branch
自动生成的webrev:https://builds.shipilev.net/patch-openjdk-epsilon-jdk/
二进制构建示例:https://builds.shipilev.net/openjdk-epsilon-jdk/
或者在Docker中:
$ docker pull shipilev/openjdk-epsilon $ docker run -it --rm shipilev/openjdk-epsilon java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xlog:gc -version [0.006s][info][gc] Initialized with 2009M heap, resizeable to up to 30718M heap with 128M steps [0.006s][info][gc] Using TLAB allocation; min: 2K, max: 4096K [0.006s][info][gc] Using Epsilon GC openjdk version "11-internal" 2018-03-20 OpenJDK Runtime Environment (build 11-internal+0-nightly-sobornost-builds.shipilev.net-epsilon-jdkX-b32) OpenJDK 64-Bit Server VM (build 11-internal+0-nightly-sobornost-builds.shipilev.net-epsilon-jdkX-b32, mixed mode) [0.071s][info][gc] Total allocated: 899 KB [0.071s][info][gc] Average allocation rate: 12600 KB/sec
配置现有GC以永远不会执行循环。例如,使用串行或并行GC应该适合相同的延迟配置文件,假设我们可以将它们各自的启发式配置为在它们面临完全堆耗尽之前从不执行GC循环(即,通过预先调整堆大小,设置非常大的年轻代大小,禁用自适应启发式等)。这很难可靠地保证它们提供的众多GC选项,以及对GC的持续改进,这将迫使我们对无操作路径进行三思考。
修改现有的GC从不进行循环。我们可以在这些GC中做出特殊选择,使其更可靠,但这可能违背了GC的设计目标。例如,保护这些GC的大多数代码路径DoNotGC
并不比提供单独的独立实现好得多。
消除现有的GC实施。另一种方法是不使用现有的GC实现来获得测试的基线实现。这样做的问题是不方便的:开发人员需要确保这样的实现仍然是正确的,它提供了足够的性能以成为一个良好的基线,它连接到其他运行时设施(堆转储,线程堆栈遍历, MXBeans)修改差异分析。其他平台的实现需要更多的工作。在主线中实现准备好的无操作实现解决了这种不便。
消除现有的GC屏障集。没有现有的替代方案可以禁用所有GC障碍,但我们可以为现有GC设置障碍集。不幸的是,它引发了与上述相同的问题,并且还因为在这种去除后禁用GC的迫切需要,因为GC期望通过障碍的基本不变量不会成立。
Parallel,G1和Shenandoah GC的进一步改进可能最终实现足够低的开销,从而不再需要无操作GC。如果发生这种情况,Epsilon仍然可用于内存压力和性能测试。
常见的GC测试不适合Epsilon GC,因为大多数测试都假设他们可以分配任意数量的垃圾。需要开发新的测试来测试GC确实在低分配工作负载上运行良好,并且它以可预测的方式在堆耗尽时失败。新的jtreg
测试hotspot/gc/epsilon
足以证明正确性。
开发过程中的一次性性能测试足以确保在使用解释器,C1和C2编译器运行时所需的性能特性。不需要进行正在进行的性能测试,因为在初始实现之后实现永远不会改变,并且其性能敏感路径由其他GC隐式测试。
实用性与维护成本。可以说,这样的实现在产品中没用,因为没有人需要它。然而,经验表明,Java生态系统中的许多玩家已经通过从定制的JVM中删除GC来完成此练习。这意味着,拥有标准的无操作GC选项将有助于生态系统的这一部分。如果实施证明微不足道,再加上低维护成本,这种风险很小。如果在“开发”标志下仅在非产品构建中仍然可用该功能,我们还认为此风险很小。用户和下游发行版可能会将其更改为“产品”或“实验性”,以将Epsilon公开给他们的应用程序。
公众期望。提供实际上不进行垃圾收集的垃圾收集器可能被视为危险的做法。生产中意外启用Epsilon GC可能会在堆耗尽时导致意外的JVM故障。如果在“开发”或“实验”选项下,在产品构建中默认情况下该功能仍然不可用,我们认为此风险很小。
地方考虑因素。非压缩GC隐式意味着它以其分配顺序维护对象图。这会对空间局部性产生影响,如果分配是随机的,或者生成大量稀疏垃圾,常规应用程序可能会遇到吞吐量损失。虽然这可能需要一些吞吐量开销,但这不在GC控制之内,并且会影响大多数不移动的GC。如果地点被证明是一个问题,则需要具有位置感知的应用程序编码来减轻这个缺点。
实施复杂性。可能的情况是,实现需要在共享代码中进行比预期更多的更改,例如在编译器和特定于平台的后端中。我们的原型表明这些变化足够孤立,是良性的。如果证明存在风险,则应通过JEP 304(“垃圾收集器接口”)进行缓解。
此工作可能依赖于JEP 304(“垃圾收集器接口”)来最小化共享代码更改。但是,如果共享代码更改最小,则可能不需要该接口。
所有者 | Lance Andersen |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 其他 - 库 |
讨论 | jdk dash dev在openjdk dot java dot net |
功夫 | 中号 |
评论人 | Alan Bateman,Alex Buckley,Brian Goetz,Mark Reinhold |
创建 | 2017/10/11 18:36 |
更新 | 2018/08/13 21:17 |
问题 | 8189188 |
从Java SE Platform和JDK中删除Java EE和CORBA模块。这些模块在Java SE 9中已弃用,声明的意图是在将来的版本中删除它们。
Java SE 6包含一个完整的Web服务堆栈,以方便Java开发人员。该堆栈由最初 为Java EE平台开发的四种技术组成:JAX-WS(基于XML的Web服务的Java API),JAXB(用于XML绑定的Java架构),JAF(JavaBeans Activation Framework)和Common Annotations。在包含时,Java SE中的版本与Java EE中的版本相同,除了Java SE在涉及Java EE安全模型的Common Annotations中删除包。但是,随着时间的推移,Java EE中的版本不断发展,这导致Java SE中的版本出现困难:
这些技术获得了与Java SE无关的功能。例如,Common Annotations在Java EE 6中添加了一个涉及Java EE容器中数据源的包。这使得有必要定期对Java EE版本进行快照和子集,这对JDK工程师来说非常耗时,并且对开发人员来说很困惑。
这些技术由java.net上的上游项目和后来的GitHub维护。由于必须将OpenJDK存储库中的Java SE版本与上游存储库中的Java EE版本同步,这使得维护成为问题。
开发人员可以从上游项目中获取独立版本的技术,并通过支持标准覆盖机制进行部署。这种长期机制允许独立版本安全地覆盖Java SE版本。不幸的是,它在实践中没有被广泛使用,开发人员使用ad-hoc机制来部署独立版本,例如将它们添加到JDK的引导类路径,或者只是将它们放在类路径上,并希望得到的拆分包将会不会引起问题。
由于Java EE技术的独立版本可以从第三方站点(例如Maven Central)获得,因此不需要Java SE平台或JDK来包含它们。
包括技术开发人员方便的Java SE的另一种情况要追溯到1998年在肯·卡瓦诺的尊敬的各位领导,Java SE的拥抱 CORBA由船OMG的CORBA API,ORB实现中,比如CosNaming实现的idlj
编译器,并支持IDL和IIOP在该rmic
编译器。但是,随着时间的推移,对CORBA的支持变得有问题:
由于CORBA是在Java Community Process之外发展的“认可标准”,因此类似于Web Services的注释适用于JDK中JDBA的维护以及安全地覆盖JDK的CORBA实现的能力。没有将JDK中的ORB与Java EE应用程序服务器中的ORB同步的现实前景。
使用Java中的CORBA开发现代应用程序没有太大的兴趣。此外,Java EE 8将CORBA,RMI-IIOP和JavaIDL列为“Proposed Optional”,表明将来可能会删除对这些技术的必需支持。
由于维护CORBA支持的成本超过了好处,因此Java SE平台或JDK不会包含它。
最后,自SE SE 5.0以来,Java SE包括自Java SE 1.3以来的JTA(Java Transaction API)子集以及J2EE活动服务(J2EE活动服务)的子集 。
JTA由两个扮演不同角色并需要不同处理的包组成:
该javax.transaction.xa
包支持JDBC中的XA事务。这个“ XA包 ”与java.sql
Java SE 9 中的模块中的JDBC位于java.sql
同一位置。由于该模块不可升级,因此独立版本的JTA不可能覆盖Java SE版本的XA包,但这是应用程序通常可以接受,因为XA包已经稳定多年,Java SE版本与Java EE版本相同。为了便于维护,Java SE中的XA软件包将来可能会被移动到另一个不可升级的模块,但作为架构问题,它将长期保留在Java SE和JDBC中,并且对此不再感兴趣。这个JEP。
该javax.transaction
包定义了一般事务管理API。该软件包的Java EE版本总是超出Java SE的范围,并且以与Java SE无关的方式发展。例如,JTA在Java EE 7中添加了与CDI相关的类型。javax.transaction
Java SE定义的子集支持与CORBA事务服务的互操作。这个“ CORBA互操作包 ”存在于java.transaction
Java SE 9 中它自己的模块中。但是,Java SE版本通常不被使用CORBA事务服务的应用程序接受,因此它们通常使用Java EE版本覆盖它。
J2EE活动服务定义了一个通用的中间件API。它自2006年以来一直没有更新,也不是Java EE平台的一部分。它仅与此JEP相关,因为Java SE包含其中一个包的子集javax.activity
,用于与CORBA事务服务进行互操作。这个“ 活动包 ”存在java.corba
于Java SE 9 的模块中。
如果没有Java SE平台或JDK中的CORBA支持,则不存在来自JTA的CORBA互操作包或来自J2EE活动服务的活动包的情况。
在Java SE 9中,包含Java EE和CORBA技术的Java SE模块被注释为不推荐删除,表示在将来的版本中删除它们的意图:
java.xml.ws
(JAX-WS,以及相关技术SAAJ和Web服务元数据)java.xml.bind
(JAXB)java.activation
(JAF)java.xml.ws.annotation
(通用注释)java.corba
(CORBA)java.transaction
(JTA)Java SE 9中的相关模块也已弃用以供删除:
java.se.ee
(上面六个模块的聚合器模块)jdk.xml.ws
(JAX-WS工具)jdk.xml.bind
(JAXB工具)由于弃用模块以进行删除只会导致编译时警告,因此JDK 9采取了更加强大的步骤,为将来版本中实际删除这些模块的开发人员做好准备:当类路径上的代码编译时,模块在JDK 9中无法解析或跑。这允许JDK 9上的开发人员在类路径上部署独立版本的Java EE和CORBA技术,就像在JDK 8上一样。或者,JDK 9 --add-modules
上的开发人员可以使用命令行上的标志来解析JDK运行时中的模块图片。
此JEP将删除上面列出的九个模块:
wsgen
和wsimport
(来自jdk.xml.ws
)schemagen
和xjc
(来自jdk.xml.bind
)idlj
,orbd
,servertool
,和tnamesrv
(来自java.corba
)CosNaming
提供程序(来自java.corba
)将不再可用。--add-modules
JDK 9也是如此。该rmic
编译器将被更新,删除-idl
和-iiop
选择。因此,rmic
将无法再生成IDL或IIOP存根和绑定类。
在JDK文档和手册页将被更新,删除这些模块和工具的任何引用,并指明了rmic
变化。
将删除所有运行Java EE或CORBA API的JDK,JCK和SQE测试。
删除Java EE模块的风险是,如果应用程序依赖于JDK for Java EE API和工具中的“开箱即用”支持,则无法编译或运行。从JDK 6,7或8迁移到JDK 9或更高版本时,这些应用程序将遇到二进制和源不兼容问题。一般来说,这些应用程序分为两类:
在Java EE应用程序服务器之外操作Web服务和XML的独立程序。
与Web服务或XML无关的应用程序,但依赖Java EE API中的各个类来实现通用功能。例如,某些应用程序依赖于JAXB而不是XML绑定,而是依赖于类提供的Base64支持javax.xml.bind.DatatypeConverter
。(从历史上看,这个类是比类更好的选择sun.misc.Base64{Encoder,Decoder}
,但更好的是java.util.Base64
Java SE 8中引入的类。)另一个例子,一些应用程序依赖@Generated
于其类型javax.annotation.Generated
与JAX-WS共存的注释。在JDK 9中。(应用程序可以选择依赖javax.annotation.processing.Generated
于Java SE 9中引入的类型)。
除去J2EE模块的另一风险在于,如果它们使用命令行标记,其已经从JDK 6,7,或8迁移到JDK 9,应用程序不启动--add-modules java.se.ee
,--add-modules java.xml.bind
等
此提议假定希望在最新JDK上编译或运行应用程序的开发人员可以查找和部署Java EE技术的备用版本。JAX-WS和JAXB的参考实现(RI)是一个很好的起点,因为它们是JDK 9中模块java.xml.ws
和java.xml.bind
模块的完全替代.RI可用作Maven工件:(注意它们必须部署在类路径上)
JAX-WS和JAXB的工具也可用作Maven工件:
wsgen
和wsimport
:com.sun.xml.ws:jaxws-tools,以及工具脚本schemagen
和xjc
:com.sun.xml.bind:jaxb-jxc和com.sun.xml.bind:jaxb-xjc,以及工具脚本还有Maven工件只包含Java EE技术的API:
在实现此JEP之后,这些API JAR文件可以部署在类路径上,就像在JDK 8和9上一样。它们也可以部署在模块路径上,以便模块化应用程序可以通过requires
指令依赖它们:
对于JAX-WS,JAXB,和SAAJ的API JAR文件是明确的模块调用java.xml.ws
,java.xml.bind
以及java.xml.soap
。
Web服务元数据的API JAR文件是一个名为的自动模块webservices.api
。(此名称源自JAR文件名,因为尚未使用Automatic-Module-Name
属性更新JAR清单。)
JAF和Common Annotations的API JAR文件是名为java.activation
和的自动模块java.annotation
。(这些名称由Automatic-Module-Name
JAR清单中的属性指定。)
在JDK 9上,描述中提到的所有模块(java.se.ee
聚合器除外)都是可升级的。这意味着使用JDK 9的开发人员--add-modules java.xml.bind
可以选择依赖JDK运行时映像中的Java EE模块,或者通过在升级模块路径上部署API JAR文件来覆盖它们。注意升级模块路径而不是模块路径的参与; 在JDK 9上的模块路径上部署API JAR文件无效,即使在--add-modules java.xml.bind
使用etc,因为JDK运行时映像中的Java EE模块优先于模块路径上具有相同名称的模块。实现此JEP后,Java EE模块将不会出现在JDK运行时映像中,因此开发人员可以在模块路径上部署API JAR文件。
删除java.corba
模块的风险是:
如果CORBA实现仅包含“认可的”CORBA API的子集并且期望JDK提供余数,则它们将不会编译或运行。
使用RMI-IIOP的应用程序和CORBA实现将无法编译或运行。RMI-IIOP软件包(javax.rmi
和javax.rmi.CORBA
)位于java.corba
模块中并与其中的CORBA实现相关联,因此一旦java.corba
删除,Java SE中就不会有RMI-IIOP支持。
使用该javax.activity
程序包的应用程序和CORBA实现将无法编译或运行。该软件包位于java.corba
模块中,并与其中的CORBA实现相关联,因此一旦java.corba
删除,Java SE中将不再支持。
除非第三方接管CORBA API,ORB实现,CosNaming提供商等的维护,否则将不会有独立版本的CORBA。第三方维护是可能的,因为Java SE平台支持CORBA的独立实现。相比之下,RMI-IIOP的API仅在Java SE中定义和实现。除非启动专用JSR来维护它,否则将不会有独立版本的RMI-IIOP,或者Eclipse的管理工作由Eclipse基金会接管。Java EE从JCP到Eclipse Foundation的管理过渡包括CORBA和RMI-IIOP的GlassFish实现。最后,没有独立版本的J2EE Activity Service。
独立版本的JTA可用作Maven工件javax.transaction:javax.transaction-api。截至2017年11月,此JAR文件代表JTA 1.2,它包含XA包和CORBA互操作包。在2018年初,JTA 1.3将被定义为仅包含CORBA互操作包; JAR文件将相应更新。JTA 1.2和JTA 1.3的JAR文件可以按如下方式部署:
JTA 1.2的JAR文件可以部署在类路径上。(忽略JAR文件中的XA包,而不是java.sql
模块中的XA包.JAR文件中的CORBA互操作包优先于java.transaction
模块中的包使用,默认情况下在JDK 9上不解析。如果--add-modules java.se.ee
或者--add-modules java.transaction
是在JDK 9中使用,则在JAR文件中的CORBA互操作包将被有利于包中的忽略java.transaction
模块。)
JTA 1.2的JAR文件可能未部署在模块路径上。(它将被视为包含XA包的自动模块,但此包将与模块中的XA包冲突java.sql
。)
JTA 1.3的JAR文件可以部署在类路径上。(JAR文件中的CORBA互操作程序包优先于java.transaction
模块中的程序包使用,默认情况下在JDK 9中未解析。)
JTA 1.3的JAR文件可以部署在模块路径上,并用作名为的自动模块java.transaction
。
所有者 | 克里斯赫加蒂 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | core-libs / java.net |
讨论 | net dash dev at openjdk dot java dot net |
功夫 | 中号 |
持续时间 | 中号 |
评论人 | Alan Bateman,Brian Goetz |
受认可 | Brian Goetz |
创建 | 2017/06/08 11:46 |
更新 | 2018/09/27 13:11 |
问题 | 8181784 |
通过JEP 110标准化JDK 9中引入的孵化 HTTP客户端API ,并在JDK 10中进行更新。
除了JEP 110的目标之外,这个JEP将:
java.net.http
包中提供基于孵育的API 的标准化 API,和这次JEP的动机与JEP 110的动机保持一致 。
此JEP建议标准化在JDK 9中作为孵化API引入并在JDK 10中更新的HTTP客户端API。孵化API已收到多轮反馈,这些反馈已导致显着改进,但在较高水平上仍然存在大致相同。API通过CompletableFuture
s 提供非阻塞请求和响应语义,可以将其链接到触发依赖操作。通过API中的平台反应流支持, 提供请求和响应主体的背压和流量控制java.util.concurrent.Flow
。
在JDK 9和JDK 10中进行孵化时,实现几乎已完全重写。该实现现在完全异步(之前的HTTP / 1.1实现正在阻塞)。RX Flow概念的使用已被推入实现中,这消除了支持HTTP / 2所需的许多原始自定义概念。现在可以更容易地跟踪数据流,从用户级请求发布者和响应订阅者一直到底层套接字。这大大减少了代码中的概念和复杂性,并最大化了HTTP / 1.1和HTTP / 2之间重用的可能性。
标准API的模块名称和包名称将是 java.net.http
。
预定义的实施BodyPublisher
,BodyHandler
以及BodySubscriber
通过静态工厂方法创建的,已经搬出去独立非实例实用工厂类,在多元化的命名约定以下。这提高了这些相对较小的接口的可读性。
静态工厂方法的名称也按以下大类更新:
fromXxx
:来自标准订阅者的适配器,例如获取 Flow.Subscriber
返回a BodySubscriber
。
ofXxx
:创建新预定义的工厂, Body[Publisher|Handler|Subscriber]
执行有用的常见任务,例如将响应主体作为String处理,或将主体流式传输到File。
other:Combinators(BodySubscriber
返回a BodySubscriber
)和其他有用的操作。
BodyHandler
s和相应的BodySubscriber
s,以提高常见场景的可用性:discard(Object replacement)
组合丢弃/忽略响应主体并允许给定的替换。反馈表明这可能看起来令人困惑。它已被删除并替换为两个单独的处理程序:1)discarding()
和2) replacing(Object replacement)
。
添加ofLines()
了返回a BodyHandler
,以Stream
逐行支持响应体的流式传输。提供与之类似的语义 BufferedReader.lines()
。
加入fromLineSubscriber
,支撑响应身体的适配到Flow.Subscriber
的String
线条。
添加BodySubscriber.mapping
用于从一种响应体类型到另一种响应体类型的通用映射
推送承诺支持已经过重新设计,以减少其对API的影响,并使其更符合常规请求/响应。具体而言,MultiSubscriber
与MultiResultMap
已被删除。推送承诺现在通过功能接口处理 PushPromiseHandler
,可选择在发送操作期间给出。
该HttpClient.Redirect
政策已被简化,通过更换 SAME_PROTOCOL
和SECURE
政策,NORMAL
。据观察,之前提到的名称SECURE
并没有真正适当命名,应该重命名为NORMAL
,因为它可能适合大多数正常情况。鉴于新命名的,如上所述,NORMAL
,SAME_PROTOCOL
出现奇怪的命名,可能是混乱,不容易被使用。
WebSocket.MessagePart
已被删除。在接收方使用此枚举来指示消息的传递是否完整。它与发送端不对称,为此目的使用简单的布尔值。另外,已经观察到用简单的布尔值处理接收的消息显着地减少并简化了接收代码逻辑。确定作为上述WHOLE
之一的利益之一和主要目的而传递的信息MessagePart
已被证明不具有其自身的重要性。
有关API的更多细节可以在JEP 110,最新的API javadoc或网络组的 JDK HTTP Client页面中找到。
孵化API的现有测试将更新为使用新的标准API。将添加其他测试以涵盖所有支持的方案,特别是HTTP / 1.1和HTTP / 2之间的升级和降级。
当前依赖于孵化的HTTP客户端API的代码将需要更新,至少要更改其包导入。这与任何其他孵育特征没有什么不同。取决于孵化模块的代码已经在编译时和运行时都收到适当的警告。
作者 | Brian Goetz |
所有者 | Vicente Arturo Romero Zaldivar |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 工具 |
讨论 | 在openjdk dot java dot net的琥珀色破折号开发 |
功夫 | XS |
持续时间 | XS |
涉及到 | JEP 286:局部变量类型推断 |
评论人 | 亚历克斯巴克利 |
创建 | 2017/12/08 15:15 |
更新 | 2018/08/23 15:44 |
问题 | 8193259 |
允许var
在声明隐式类型的lambda表达式的形式参数时使用。
一个lambda表达式可以被隐式类型,其中类型的所有形式参数都推断出:
(x, y) -> x.process(y) // implicitly typed lambda expression
Java SE 10使隐式类型可用于局部变量:
var x = new Foo(); for (var x : xs) { ... } try (var x = ...) { ... } catch ...
为了与局部变量保持一致,我们希望允许'var'用于隐式类型的lambda表达式的形式参数:
(var x, var y) -> x.process(y) // implicit typed lambda expression
统一性的一个好处是修饰符,特别是注释,可以应用于局部变量和lambda形式,而不会失去简洁性:
@Nonnull var x = new Foo(); (@Nonnull var x, @Nullable var y) -> x.process(y)
对于隐式类型的lambda表达式的形式参数,允许使用保留的类型名称var
,以便:
(var x, var y) -> x.process(y)
相当于:
(x, y) -> x.process(y)
隐式类型化的lambda表达式必须var
用于其所有形式参数,或者不能用于任何形式参数。此外,var
仅允许隐式类型化lambda表达式的形式参数---显式类型化的lambda表达式继续为其所有形式参数指定清单类型,因此某些形式参数不允许其他人使用清单类型var
。以下示例是非法的:
(var x, y) -> x.process(y) // Cannot mix 'var' and 'no var' in implicitly typed lambda expression (var x, int y) -> x.process(y) // Cannot mix 'var' and manifest types in explicitly typed lambda expression
从理论上讲,可能有一个lambda表达式,如上面的最后一行,它是半显式类型(或半隐式类型,取决于你的观点)。但是,它超出了此JEP的范围,因为它会深刻影响类型推断和重载决策。这是保持lambda表达式必须指定所有清单参数类型或不指定的限制的主要原因。我们还想强制推断隐式类型化lambda表达式的参数的推断类型是否相同无论是否var
使用。我们可能会在未来的JEP中回到局部推断的问题。此外,我们不希望损害简写语法的简洁性,因此我们不允许使用以下表达式:
var x -> x.foo()
继续声明隐式类型的lambda表达式,如Java SE 8中所示。
var
在隐式类型化的lambda表达式中的参数名称之前添加时,此JEP不存在源不兼容的风险,因为参数var
的推断类型与推断的类型相同var
。
所有者 | 亚当佩彻 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | security-libs / javax.crypto |
讨论 | openjdk dot java dot net的安全破折号 |
功夫 | 小号 |
持续时间 | 中号 |
评论人 | Brian Goetz,肖恩穆兰 |
受认可 | Brian Goetz |
创建 | 2017/06/05 15:40 |
更新 | 2018/09/13 15:15 |
问题 | 8181595 |
使用RFC 7748中描述的Curve25519和Curve448实现密钥协议 。
RFC 7748定义了一种密钥协商方案,该方案比现有的椭圆曲线Diffie-Hellman(ECDH)方案更有效和安全。此JEP的主要目标是API和此标准的实现。其他实施目标是:
RFC 7748只能在SunEC提供商中实现。此JEP的目标不是在其他提供商中实施此标准。
SunEC中的实现不支持任意域参数。JCA API应通过扩展允许任意域参数的规范。此类扩展超出了本JEP的范围。
由于其安全性和性能特性,使用Curve25519和Curve448的密码学需求旺盛。许多其他加密库(如OpenSSL,BoringSSL和BouncyCastle)已经支持使用这些曲线进行密钥交换。此密钥交换机制是TLS 1.3的可选组件,并通过常用扩展在早期TLS版本中启用。
在RFC 7748中所描述的X25519和X448的功能将被执行,而这些功能将被用于实施新的KeyAgreement
,KeyFactory
以及KeyPairGenerator
在现有SunEC提供服务。该实现将使用RFC 7748中描述的恒定时间蒙哥马利梯形图方法,以防止侧信道攻击。通过将结果与0进行比较,实现将确保贡献行为,如RFC中所述。
将使用新的模块化算术库执行大数字运算。该库将具有两个显着优势BigInteger
:
这个新库将在一个内部JDK包中,并且只能由新的加密算法使用。我们期望在不久的将来在EdDSA(使用Curve25519和Curve448的签名)和Poly1305(消息认证)实现中使用它。该库使用了受EdDSA论文启发的简化基数表示。
因为算法避免了进位操作,如果它有错误或使用不正确,它可能会溢出并产生不正确的结果。例如,加法运算不携带,因此可以在每次乘法运算之前执行有限数量(通常为一个)的加法运算。该库将包含防止滥用的防御(例如,如果发生太多添加,则抛出异常),并且必须仔细测试以确保它不会溢出。
为RFC 7748的JCA API将使用名称“XDH”来标识与此相关的机制(所有服务KeyAgreement
,KeyPairGenerator
,KeyFactory
等)。算法名称“X25519”和“X448”也将分别定义为使用Curve25519和Curve448的XDH。这允许方便的简写(例如KeyPairGenerator.getInstance("X448")
),这也使得更容易定位支持期望曲线的提供者。像“X25519”和“X448”这样的名称不应该只是“XDH”的别名---应该使用正确的曲线初始化为这些名称返回的服务,并且它可以拒绝使用不同曲线的任何键。
AlgorithmParameterSpec
:一个名为的新类NamedParameterSpec
将用于指定使用哪条曲线(X25519或X448)。此类使用单个标准名称来指定一组参数,并且它将由其他使用命名参数的算法重用。例如,它可以用于(有限域)Diffie-Hellman中的命名组。NamedParameterSpec
将被插入到上面的类层次结构中ECGenParameterSpec
,这样也ECGenParameterSpec
将是一个NamedParameterSpec
。
KeySpec
:新类XECPublicKeySpec
可用于指定公钥。该类有一个BigInteger
成员,它保存u
点的坐标。新类XECPrivateKeySpec
可用于指定私钥。该类有一个字节数组成员,它保存k
RFC 7748中描述的X25519和X448函数的(编码)输入。这两个KeySpec
类都有一个AlgorithmParameterSpec
指定曲线和其他算法参数的成员。
现有X509EncodedKeySpec
类也可用于公钥。现有PKCS8EncodedKeySpec
类也可用于私钥。
Key
interfaces:将添加新接口XECPublicKey
,XECPrivateKey
以提供对密钥对象中包含的信息的访问。在这些接口中的关键数据的表示将等同于在表示中XECPublicKeySpec
和XECPrivateKeySpec
。两个接口都将从新接口扩展XECKey
,这将提供对AlgorithmParameterSpec
定义曲线和其他算法参数的访问。
示例API用法:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH"); NamedParameterSpec paramSpec = new NamedParameterSpec("X25519"); kpg.initialize(paramSpec); // equivalent to kpg.initialize(255) // alternatively: kpg = KeyPairGenerator.getInstance("X25519") KeyPair kp = kpg.generateKeyPair(); KeyFactory kf = KeyFactory.getInstance("XDH"); BigInteger u = ... XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u); PublicKey pubKey = kf.generatePublic(pubSpec); KeyAgreement ka = KeyAgreement.getInstance("XDH"); ka.init(kp.getPrivate()); ka.doPhase(pubKey, true); byte[] secret = ka.generateSecret();
考虑了与实施有关的一些备选方案:
BigInteger
。初始原型设计表明它BigInteger
比自定义模块化算术库慢大约10倍。例如,X25519 BigInteger
在初始测试中在同一平台上运行的自定义库需要大约2.5毫秒,而0.25毫秒。此外,BigInteger
没有恒定时间乘法,这将消除这些功能的一些侧通道电阻。测试将包括来自RFC 7748的测试向量。这些向量包括X25519和X448函数的一百万次迭代,这将花费大约15分钟来完成。如果我们想要定期运行这些,我们可以通过将它们分成10-100万个批次来并行化它们。
测试算术库将更具挑战性,因为导致溢出的条件可能不会自然发生。用于Curve25519和Curve448的表示应该与严格的数学证明一起开发,它们不会溢出。这些证明将包含与每个基础操作(加,乘,进,减)相关的边界条件,这些条件可以包含在回归测试中。
一个重要的风险是模块化算术实现的复杂性和微妙性。通过开发严格的算术正确性证明和彻底的测试,可以减轻这种风险。
所有者 | 拉赫纳戈尔 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | core-libs / java.lang |
讨论 | 在openjdk dot java dot net的i18n dash dev |
功夫 | 中号 |
持续时间 | 中号 |
评论人 | Naat Sato,Alan Bateman |
受认可 | Brian Goetz |
创建 | 2017/06/19 16:43 |
更新 | 2018/08/07 09:18 |
问题 | 8182490 |
升级现有的平台API,支持10.0版本中的Unicode标准。
支持最新版本的Unicode,主要在以下类中:
Character
并且String
在java.lang
封装NumericShaper
在java.awt.font
包中,和Bidi
,BreakIterator
和Normalizer
在java.text
包中。此JEP将不会实现四个相关的Unicode规范:
Unicode是一个不断发展的行业标准,因此我们必须将Java与最新版本保持一致。
Java SE 10实现了Unicode 8.0。Unicode 9.0增加了7,500个字符和6个新脚本,Unicode 10.0.0增加了8,518个字符和4个新脚本。此升级将包括Unicode 9.0更改,因此将添加总共16,018个字符和10个新脚本。
作者 | MarkusGrönlund,Erik Gahlin |
所有者 | 艾瑞克加林 |
类型 | 特征 |
范围 | JDK |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 热点/ jfr |
讨论 | openjdk dot java dot net的hotspot dash dev |
功夫 | 大号 |
持续时间 | 中号 |
评论人 | David Holmes,Karen Kinnear,Mikael Vidstedt |
受认可 | Mikael Vidstedt |
创建 | 2017/12/12 19:13 |
更新 | 2018/09/09 16:43 |
问题 | 8193393 |
提供低开销的数据收集框架,用于对Java应用程序和HotSpot JVM进行故障排除。
故障排除,监视和分析是开发生命周期中不可或缺的部分,但是在涉及实际数据的繁重负载下,某些问题仅发生在生产中。
Flight Recorder记录源自应用程序,JVM和OS的事件。事件存储在一个文件中,该文件可以附加到错误报告中并由支持工程师进行检查,允许事后分析导致问题的时期内的问题。工具可以使用API从记录文件中提取信息。
JEP 167:基于事件的JVM跟踪向HotSpot JVM添加了一组初始事件。Flight Recorder将扩展为Java创建事件的能力。
JEP 167还添加了一个基本的后端,其中事件的数据打印到stdout。Flight Recorder将提供单个高性能后端,用于以二进制格式编写事件。
模块:
jdk.jfr
java.base
(适用于资源受限的设备)jdk.management.jfr
jdk.jfr
和jdk.management
可以在命令行上启动Flight Recorder:
$ java -XX:StartFlightRecording ...
也可以使用bin / jcmd工具启动和控制录制:
$ jcmd JFR.start $ jcmd JFR.dump filename=recording.jfr $ jcmd JFR.stop
此功能通过JMX远程提供,对Mission Control等工具非常有用。
用户可以使用API创建自己的事件:
import jdk.jfr.*; @Label("Hello World") @Description("Helps the programmer getting started") class HelloWorld extends Event { @Label("Message") String message; } public static void main(String... args) throws IOException { HelloWorld event = new HelloWorld(); event.message = "hello, world!"; event.commit(); }
可以使用以下类中的类从记录文件中提取数据 jdk.jfr.consumer
:
import java.nio.file.*; import jdk.jfr.consumer.*; Path p = Paths.get("recording.jfr"); for (RecordedEvent e : RecordingFile.readAllEvents(p)) { System.out.println(e.getStartTime() + " : " + e.getValue("message")); }
线程将事件(无锁)写入线程局部缓冲区。一旦线程本地缓冲区填满,它将被提升为全局内存循环缓冲系统,该系统维护最新的事件数据。根据配置,最旧的数据将被丢弃或写入磁盘,从而可以连续保存历史记录。磁盘上的二进制文件具有扩展名.jfr
,并使用保留策略进行维护和控制。
事件模型以自描述二进制格式实现,以小端基128编码(文件头和一些附加部分除外)。二进制数据格式不能直接使用,因为它可能会发生变化。相反,将提供API以与记录文件交互。
作为说明性示例,类加载事件包含描述其发生的时间戳,描述时间跨度的持续时间,线程,堆栈跟踪以及三个事件特定的有效载荷字段,加载的类和关联的类加载器。事件的大小总共为24个字节。
: 98 80 80 00 87 02 95 ae e4 b2 92 03 a2 f7 ae 9a 94 02 02 01 8d 11 00 00
[98 80 80 00]
[87 02]
[95 ae e4 b2 92 03]
[a2 f7 ae 9a 94 02]
[02]
[01]
[0x8d11]
[0]
[0]
可以启用,禁用和过滤事件,以减少开销和存储所需的空间量。这可以使用以下设置完成:
有两个配置集可以为低开销,开箱即用的用例配置Flight Recorder。用户可以轻松创建自己的特定事件配置。
将添加涵盖以下领域的活动:
Flight Recorder的替代方案是记录。尽管JEP 158:统一JVM日志记录在HotSpot JVM中的子系统之间提供了一定程度的一致性,但它并未扩展到Java应用程序和JDK库。传统上,日志记录通常缺乏明确的模型和元数据,使其成为自由形式,其结果是消费者必须与内部格式紧密耦合。没有关系模型,很难保持数据紧凑和规范化。
Flight Recorder维护一个类型化的事件模型,消费者通过使用API与内部组件分离。
将需要进行性能测试以确保可接受的开销水平。
特定于供应商的后端可能是在JEP 167之上开发的; 工作假设是飞行记录器基础设施应该涵盖大多数现有用例。鼓励供应商在本JEP的背景下讨论如何建议移动到单个后端的可行性。
Flight Recorder已经存在多年,以前是Oracle JDK的商业功能。此JEP将源代码移动到打开的存储库以使该功能通常可用。因此,兼容性,性能,回归和稳定性的风险很低。
所有者 | 贾米尔尼玛 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | security-libs / javax.crypto |
讨论 | openjdk dot java dot net的安全破折号 |
功夫 | 小号 |
持续时间 | 小号 |
评论人 | Brian Goetz,肖恩穆兰 |
受认可 | Brian Goetz |
创建 | 2016/03/29 20:15 |
更新 | 2018/09/13 15:19 |
问题 | 8153028 |
按RFC 7539中的规定实施ChaCha20和ChaCha20-Poly1305密码。ChaCha20是一种相对较新的流密码,可以取代旧的,不安全的RC4流密码。
Cipher
实现。这些算法将在SunJCE提供程序中实现。KeyGenerator
实现,创建适合ChaCha20和ChaCha20-Poly1305算法的密钥。AlgorithmParameters
与ChaCha20-Poly1305算法一起使用的实现。TLS密码套件支持不会成为此JEP的一部分。TLS对这些密码的支持将成为后续增强的一部分。
唯一其他广泛采用的流密码RC4长期以来一直被认为是不安全的。业界一致认为ChaCha20-Poly1305在这个时间点是安全的,并且它已经在TLS实现以及其他加密协议中得到了相当广泛的采用。JDK需要与其他加密工具包和TLS实现相提并论。
此外,TLS 1.3仅允许使用基于AEAD的密码套件。实现ChaCha20-Poly1305算法是实现在AEAD模式下运行的不同密码套件的第一步,以防在AES或GCM中发现任何弱点。
ChaCha20和ChaCha20-Poly1305算法将javax.crypto.CipherSpi
在SunJCE提供程序中实现API。使用该Cipher.getInstance()
方法将以与其他密码相同的方式实例化密码。对于这两种密码,允许两种可接受的转换。单名转换是首选方法,"ChaCha20"
ChaCha20是一个没有认证的简单流密码,"ChaCha20-Poly1305"
ChaCha20是使用Poly1305作为认证者的AEAD密码。 "ChaCha20/None/NoPadding"
并且"ChaCha20-Poly1305/None/NoPadding"
也可以接受变换字符串,虽然没有其它模式或填充值除了"None"
和"NoPadding"
将被接受。使用其他模式或填充值将导致抛出异常。
ChaCha20密码的初始化将接受新的AlgorithmParameterSpec
实现,javax.crypto.spec.ChaCha20ParameterSpec
:
ChaCha20ParameterSpec(byte[] nonce, int counter); // Constructor public byte[] getNonce(); // Obtains a copy of the nonce value public int getCounter(); // Obtains the initial counter value
nonce值的长度必须为96位(12字节)。任何其他长度都会导致抛出异常。整数计数器值可以是任何整数值,甚至是负数,以便允许全范围的无符号32位值。
在没有a的情况下初始化该算法ChaCha20ParameterSpec
将使密码在内部生成其自己的12字节随机数并将计数器值设置为1.可以通过调用该Cipher.getIV()
方法来获得计数器字节。
可以通过提供javax.crypto.spec.IvParameterSpec
包含12字节随机数的当前类的实例来完成ChaCha20-Poly1305的初始化。使用IvParameterSpec
过的决定ChaCha20ParameterSpec
允许ChaCha20-Poly1305被反向移植到早期版本而不进行任何API更改。因为IvParameterSpec
没有对它所保存的字节设置长度要求,所以密码对象本身将在初始化期间强制执行12字节长度要求。
与ChaCha20一样,ChaCha20-Poly1305可以在没有a的IvParameterSpec
情况下进行初始化,在这种情况下,随机数将随机生成,并且可以使用Cipher.getIV()
。
通过任何init
方法提供的关键对象必须具有算法类型“ChaCha20”。KeyGenerator
将创建一个新的实现来支持这一点。与KeyGenerator
其他算法(如AES,RC2,ARCFOUR和HmacSHA2系列)的现有实现一样,这KeyGenerator
可能无法用a初始化AlgorithmParameterSpec
。如果init
调用允许可调整密钥长度的方法形式,则该参数必须设置为256或InvalidParameterException
将抛出一个。
使用ChaCha20算法遵循Cipher
用于其他流密码的现有API。简单的单部分加密可以编写如下:
// Get a Cipher instance and set up the parameters // Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText" // are coming from outside this code snippet Cipher mambo = Cipher.getInstance("ChaCha20"); ChaCha20ParameterSpec mamboSpec = new ChaCha20ParameterSpec(nonceBytes, 7); // Use a starting counter value of "7" // Encrypt our input mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec); byte[] encryptedResult = mambo.doFinal(pText);
对于使用Poly1305身份验证器在AEAD模式下运行的ChaCha20,只需要nonce,因为RFC 7539定义了从1开始的数据的初始计数器值。为了使此Cipher实现可以向后移植并便于在我们的JSSE提供程序中使用,javax.crypto.spec.IvParameterSpec
将用于提供nonce。
在AEAD模式下运行时,由于添加了认证标签(用于加密)或标签的消耗和验证(用于解密),输出大小可能与输入不同。在需要在加密/解密之前分配输出缓冲器的情况下,getOutputSize()
应该使用该方法。以下是一个单部分加密示例:
// Get a Cipher instance and set up the parameters // Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText" // are coming from outside this code snippet Cipher mambo = Cipher.getInstance("ChaCha20-Poly1305"); AlgorithmParameterSpec mamboSpec = new IvParameterSpec(nonceBytes); // Encrypt our input mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec); byte[] encryptedResult = new byte[mambo.getOutputSize(pText.length)]; mambo.doFinal(pText, 0, pText.length, encryptedResult);
ChaCha20和ChaCha20-Poly1305密码的一个重要要求是,在doFinal()
呼叫之后,init()
必须进行新的呼叫,该呼叫提供与当前配置的nonce不同的nonce。这与AES-GCM的加密要求类似,但在这两种密码的情况下,init要求必须在加密和解密操作之后发生。后续调用Cipher.update()
,Cipher.updateAAD()
或Cipher.doFinal()
一前一后doFinal()
,并没有init()
通话在两者之间将导致IllegalStateException
被抛出。
测试将涵盖以下领域:
唯一重要的依赖是常量数学API。这些将作为JEP 324的一部分提供。
所有者 | 乔纳森吉本斯 |
类型 | 特征 |
范围 | JDK |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 工具/ javac |
讨论 | openjdk dot java dot net的编译器dash dev |
评论人 | Alan Bateman,Alex Buckley,Mandy Chung |
受认可 | Brian Goetz |
创建 | 2017/12/01 19:04 |
更新 | 2018/09/10 21:14 |
问题 | 8192920 |
增强java
启动程序以运行作为单个Java源代码文件提供的程序,包括通过“shebang”文件和相关技术在脚本中使用 。
更改Java语言规范(JLS)或javac以适应shebang文件不是目标。同样,将Java语言发展为通用脚本语言也不是目标。
这个JEP的目标不是改变Java语言规范以适应编写小程序的简单方法,例如不需要标准public static void main(String[] args)
方法。但是,预计对Java语言的任何此类更改都将与此功能结合使用。
单文件程序 - 整个程序适合单个源文件 - 在学习Java的早期阶段和编写小型实用程序时很常见。在这种情况下,在运行程序之前必须编译程序是纯粹的仪式。此外,单个源文件可能会编译为多个类文件,这会增加“运行此程序”这一简单目标的打包开销。希望能够使用java
启动器直接从源代码运行程序:
java HelloWorld.java
从JDK 10开始,java
启动器以三种模式运行:启动类文件,启动JAR文件的主类,或启动模块的主类。这里我们添加一个新的第四种模式:启动在源文件中声明的类。
源文件模式是通过在命令行上考虑两个项目来确定的:
--source
版本的选项,如果存在的话。如果“类名”标识具有.java
扩展名的现有文件,则选择源文件模式,并编译并运行该文件。该--source
选项可用于指定源代码的源版本。
如果文件没有.java
扩展名,则--source
必须使用该选项强制执行源文件模式。这适用于诸如源文件是要执行的“脚本”并且源文件的名称不遵循Java源文件的常规命名约定的情况。(参见下面的“shebang”文件。)
使用该--source
选项时,还必须使用该选项指定源代码的源版本--enable-preview
。(见JEP 12。)
在源文件模式下,效果就像源文件被编译到内存中一样,并且执行源文件中找到的第一个类。例如,如果调用的文件HelloWorld.java
包含一个名为的类 hello.World
,那么该命令
java HelloWorld.java
是非正式的等同于
javac -d HelloWorld.java java -cp hello.World
在原始命令行中放置源文件名后面的任何参数在执行时都会传递给已编译的类。例如,如果调用的文件Factorial.java
包含一个被调用的类Factorial
来计算其参数的阶乘,那么该命令
java Factorial.java 3 4 5
是非正式的等同于
javac -d Factorial.java java -cp Factorial 3 4 5
在源文件模式下,任何其他命令行选项按如下方式处理:
启动程序扫描源文件之前指定的选项,以查找与编译源文件相关的任何选项。这包括:--class-path
, --module-path
,--add-exports
,--add-modules
,--limit-modules
, --patch-module
,--upgrade-module-path
,和这些选项中的任何变异形式。它还包括JEP 12中--enable-preview
描述的新选项 。
没有规定将任何其他选项传递给编译器,例如-processor
或-Werror
。
命令行参数文件(@ -files)可以以标准方式使用。可以将VM或被调用程序的长参数列表放在命令行中指定的文件中,方法是在文件名前加上一个@
字符。
在源文件模式下,编译过程如下:
任何与编译环境相关的命令行选项都会被考虑在内。
没有找到并编译其他源文件,就好像源路径设置为空值一样。
注释处理被禁用,就像-proc:none
生效一样。
如果指定了版本,则通过该--source
选项将该值用作--release
编译的隐式选项的参数。这将设置编译器接受的源版本和源文件中的代码可能使用的系统API。
源文件在未命名的模块的上下文中编译。
源文件应包含一个或多个顶级类,第一个类作为要执行的类。
编译器不强制执行JLS§7.6末尾定义的可选限制 ,即命名包中的类型应存在于文件中,该文件的名称由类型名称后跟.java
扩展名组成。
如果源文件包含错误,则会将相应的错误消息写入标准错误流,并且启动程序将以非零退出代码退出。
在源文件模式下,执行如下:
要执行的类是源文件中找到的第一个顶级类。它必须包含标准public static void main(String[])
方法的声明 。
编译的类由自定义类加载器加载,该加载器委托给应用程序类加载器。(这意味着出现在应用程序类路径上的类不能引用源文件中声明的任何类。)
已编译的类在未命名的模块的上下文中执行,并且好像--add-modules=ALL-DEFAULT
是有效的(除了--add-module
可能已在命令行上指定的任何其他 选项。)
出现在命令行上的文件名后面的任何参数都以main
明显的方式传递给标准方法。
如果应用程序类路径上有一个类,其名称与要执行的类的名称相同,则会出错。
请注意,使用简单的命令行时可能存在轻微的歧义java HelloWorld.java
。以前,HelloWorld.java
本来会被解释为java
在一个被调用的包中调用的类HelloWorld
,但是现在它被解析为支持一个被调用的文件,HelloWorld.java
如果这样的文件存在的话。鉴于这样的类名和这样的包名都违反了几乎普遍遵循的命名约定,并且考虑到这样一个类在类路径上的不可靠性以及在当前目录中的同名文件,这似乎是一个可接受的妥协
源文件模式需要存在jdk.compiler
模块。当Foo.java
请求文件的源文件模式时,启动器的行为就像命令行被转换为:
java [VM args] \ -m jdk.compiler/ \ Foo.java [program args]
source-launcher实现类以编程方式调用编译器,编译器将源编译为内存中表示。然后,source-launcher实现类创建一个类加载器,以从该内存中表示加载已编译的类,并调用main(String[])
源文件中找到的第一个顶级类的标准方法。
source-launcher实现类可以访问任何相关的命令行选项,例如定义类路径,模块路径和模块图的选项,并将这些选项传递给编译器以配置编译环境。
如果调用的类抛出异常,则将该异常传递回启动程序以便以正常方式进行处理。但是,从异常的堆栈跟踪中删除了导致类执行的初始堆栈帧。目的是异常的处理类似于如果类已由启动程序本身直接执行的处理。初始堆栈帧将在任何对堆栈的直接访问中可见,包括(例如)Thread.dumpStack()
。
用于加载已编译类的类加载器本身对任何引用类加载器定义的资源的URL使用特定于实现的协议。获取此类URL的唯一方法是使用类似getResource
或的方法getResources
; 不支持从字符串创建任何此类URL。
当手头的任务需要一个小的实用程序时,单文件程序也很常见。在这种情况下,希望能够使用“#!” 直接从源代码运行程序。 Unix衍生系统上的机制,例如macOS和Linux。这是操作系统提供的一种机制,它允许将单个文件程序(例如脚本或源代码)放在任何方便命名的第一行开头的可执行文件中,#!
并指定要执行的程序的名称。 “文件的内容。这些文件称为“shebang文件”。
希望能够用这种机制执行Java程序。
使用源文件模式调用Java启动程序的shebang文件必须以下列内容开头:
#!/path/to/java --source version
例如,我们可以获取“Hello World”程序的源代码,并将其放在一个名为的文件中hello
,在初始行之后 #!/path/to/java --source 10
,然后将该文件标记为可执行文件。然后,如果文件在当前目录中,我们可以使用以下命令执行它:
$ ./hello
或者,如果文件位于用户PATH的目录中,我们可以使用以下命令执行:
$ hello
该命令的任何参数都将传递给main
执行的类的方法。例如,如果我们将程序的源代码放入一个名为factor的计算中factorial
,我们可以使用如下命令执行它:
$ factorial 6
--source
在以下情况下,必须在shebang文件中使用该选项:
--source
应首先在可执行文件的名称后指定该选项。启动程序也可以使用其他选项显式调用shebang文件,命令如下:
$ java -Dtrace=true --source 10 factorial 3
Java启动程序的源文件模式为shebang文件提供了两种选择:
当启动程序读取源文件时,如果该文件不是Java源文件(即它不是名称以文件结尾的文件.java
)并且如果第一行开头#!
,则该行的内容最多但不包括第一行在确定要传递给编译器的源代码时,将忽略换行符。出现在第一行之后的文件内容必须包含Java语言规范CompilationUnit
版本中的第7.3节定义的有效内容,该版本适用于选项中给出的平台版本(如果存在)或版本如果该 选项不存在,则用于运行程序的平台。--source
--source
保留第一行末尾的换行符,以便任何编译器错误消息中的行号在shebang文件中有意义。
某些操作系统将可执行文件的名称后面的第一行文本作为可执行文件的单个参数传递。考虑到这一点,如果启动器遇到一个开始--source
并包含空格的选项,它将被分成一系列单词,由空格分隔,然后由启动器进一步分析。这允许将额外的参数放在第一行,尽管某些操作系统可能会对行的总长度施加限制。不支持使用引号来保留此类值中的空格。
无需更改JLS即可支持此功能。
在一个shebang文件中,前两个字节必须是0x23 0x21
,两个字符的ASCII编码#!
。使用有效的默认平台字符编码读取所有后续字节。
#!
只有在需要使用操作系统的shebang机制执行文件时才需要第一行开头。当显式使用Java启动程序在源文件中运行代码时,不需要任何特殊的第一行,如上面给出的HelloWorld.java
和Factorial.java
示例中所示。实际上,不允许使用shebang机制来执行遵循Java源文件的标准命名约定的文件。
现状已经有20多年了; 我们可以继续这样做。
#!
可以配置支持shebang文件的系统使用不同的前缀,而不是使用//!
。这样的前缀将被视为javac
单行注释,并且不需要任何特殊处理来忽略它。但是,在macOS和Linux等操作系统上引入新的 幻数需要手动或自动更新此类系统,这超出了本JEP的范围。
可以将包含Java源代码的shell脚本编写为可以传递给Java源代码启动器的here文档,而不是使用shebang机制 。虽然这最终是比shebang机制更灵活的机制,但在简单的情况下它也比使用shebang更开销。
我们可以创建一个源启动器,但除此之外还要调用它java
,例如jrun
。鉴于发射器已经具有的执行模式的数量,这可能被视为无偿的差异。
我们可以将“一次性运行”的任务委托给该jshell
工具。虽然这可能起初看起来很明显,但这在设计中是一个明确的非目标 jshell
。该jshell
工具被设计为交互式外壳,并且许多设计决策都有利于提供更好的交互式体验。由于成为批处理运行程序的额外限制而增加负担会减损交互式体验。
我们也可以使用这个jrunscript
工具。但是,此工具提供了与运行时环境交互的有限工具,并未解决提供使用Java的简单介绍的愿望。
所有者 | 让·克里斯托弗·贝勒 |
类型 | 特征 |
范围 | JDK |
状态 | 关闭/交付 |
发布 | 11 |
零件 | hotspot / jvmti |
讨论 | openjdk dot java dot net的hotspot dash dev |
功夫 | 大号 |
评论人 | Mikael Vidstedt,Robbin Ehn,Serguei Spitsyn |
受认可 | Mikael Vidstedt,Vladimir Kozlov |
创建 | 2016/12/12 21:31 |
更新 | 2018/09/05 19:16 |
问题 | 8171119 |
提供一种低开销的Java堆分配采样方法,可通过JVMTI访问。
提供一种从JVM获取有关Java对象堆分配的信息的方法:
用户非常需要了解其堆的内容。糟糕的堆管理可能导致堆耗尽和GC抖动等问题。因此,已经开发了许多工具来允许用户反省他们的堆,例如Java Flight Recorder jmap
,YourKit和VisualVM工具。
大多数现有工具缺少的一条信息是特定分配的呼叫站点。堆转储和堆直方图不包含此信息。这些信息对于调试内存问题至关重要,因为它告诉开发人员他们的代码中发生的特定(特别是坏)分配的确切位置。
目前有两种方法可以从HotSpot中获取此信息:
首先,您可以使用字节码重写器(例如Allocation Instrumenter)来检测应用程序中的所有分配。然后,您可以让检测器进行堆栈跟踪(当您需要时)。
其次,您可以使用Java Flight Recorder,它可以在TLAB重新填充时以及直接分配给旧代时进行堆栈跟踪。这样做的缺点是:a)它与特定的分配实现(TLAB)相关联,并且错过了不符合该模式的分配; b)它不允许用户自定义采样间隔; c)它只记录分配,因此你无法区分实时和死对象。
该提议通过提供可扩展的JVMTI接口来缓解这些问题,该接口允许用户定义采样间隔并返回一组实时堆栈跟踪。
这里提出的用于堆采样特性的API的用户包括JVMTI的扩展,允许进行堆分析。以下系统依赖于事件通知系统,该系统将提供回调,例如:
void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jobject object, jclass object_klass, jlong size)
哪里:
thread
是分配的线程jobject
,object
是抽样的参考jobject
,object_klass
对于类jobject
,和size
是分配的大小。新API还包括一个新的JVMTI方法:
jvmtiError SetHeapSamplingInterval(jvmtiEnv* env, jint sampling_interval)
其中sampling_interval
是采样之间的平均分配字节数。该方法的规范是:
sampling_interval
字节 采样间隔向用户发送回调
sampling_interval
1024 * 1024。请注意,采样间隔不精确。每次发生采样时,选择下一个采样之前的字节数将是伪随机的,具有给定的平均间隔。这是为了避免采样偏差; 例如,如果每512KB发生相同的分配,则512KB采样间隔将始终采样相同的分配。因此,尽管采样间隔并不总是所选择的间隔,但在大量样本之后,它将倾向于它。
要启用此功能,用户可以使用通常的事件通知调用:
jvmti->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL)
在初始化和正确设置分配时将发送事件,因此在实际代码执行分配之后会略微发生。默认情况下,平均采样间隔为512KB。
以使采样事件系统所需的最小是调用SetEventNotificationMode
与JVMTI_ENABLE
和事件类型JVMTI_EVENT_SAMPLED_OBJECT_ALLOC
。要修改采样间隔,用户调用该SetHeapSamplingInterval
方法。
要禁用系统,
jvmti->SetEventNotificationMode(jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL)
禁用事件通知并自动禁用采样器。
通过再次调用采样器SetEventNotificationMode
将使用当前设置的任何采样间隔(默认为512KB或用户通过的最后一个值SetHeapSamplingInterval
)重新启用采样器。
为了保护新功能并使其成为VM实现的可选项,将can_generate_sampled_object_alloc_events
引入一个名为的新功能jvmtiCapabilities
。
使用通知系统提供了仅为特定线程发送事件的直接方法。这是通过SetEventNotificationMode
并为要修改的线程提供第三个参数来完成的。
以下部分提供了代码片段来说明采样器的API。首先,启用功能和事件通知:
jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.SampledObjectAlloc = &SampledObjectAlloc; jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); caps.can_generate_sampled_object_alloc_events = 1; if (JVMTI_ERROR_NONE != (*jvmti)->AddCapabilities(jvmti, &caps)) { return JNI_ERR; } if (JVMTI_ERROR_NONE != (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL)) { return JNI_ERR; } if (JVMTI_ERROR_NONE != (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(jvmtiEventCallbacks)) { return JNI_ERR; } // Set the sampler to 1MB. if (JVMTI_ERROR_NONE != (*jvmti)->SetHeapSamplingInterval(jvmti, 1024 * 1024)) { return JNI_ERR; }
要禁用采样器(禁用事件和采样器):
if (JVMTI_ERROR_NONE != (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL)) { return JNI_ERR; }
要以1024 * 1024字节采样间隔重新启用采样器,需要启用该事件的简单调用:
if (JVMTI_ERROR_NONE != (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL)) { return JNI_ERR; }
生成事件时,回调可以使用JVMTI GetStackTrace
方法捕获堆栈跟踪。通过回调获得的jobject引用也可以包装到JNI弱引用中,以帮助确定对象何时被垃圾回收。这种方法允许用户收集有关采样对象的数据,以及仍被认为是实时对象的数据,这可能是理解作业行为的好方法。
例如,可以这样做:
extern "C" JNIEXPORT void JNICALL SampledObjectAlloc(jvmtiEnv *env, JNIEnv* jni, jthread thread, jobject object, jclass klass, jlong size) { jvmtiFrameInfo frames[32]; jint frame_count; jvmtiError err; err = global_jvmti->GetStackTrace(NULL, 0, 32, frames, &frame_count); if (err == JVMTI_ERROR_NONE && frame_count >= 1) { jweak ref = jni->NewWeakGlobalRef(object); internal_storage.add(jni, ref, size, thread, frames, frame_count); } }
其中internal_storage是一个可以处理采样对象的数据结构,考虑是否需要清理任何垃圾收集样本等。该实现的内部是特定于用途的,并且超出了此JEP的范围。
采样间隔可用作减轻分析开销的手段。采样间隔为512KB时,开销应足够低,以便用户可以合理地保持系统处于打开状态。
目前的原型和实施证明了该方法的可行性。它包含五个部分:
ThreadLocalAllocationBuffer
(TLAB)结构中字段名称的更改而导致体系结构相关的更改。这些更改很小,因为它们只是名称更改。allocation_end
指针进行扩充,以补充现有end
指针。如果禁用采样,则两个指针始终相等,代码执行与以前一样。如果启用了采样,则将其end
修改为请求下一个采样点的位置。然后,任何快速路径将“认为”TLAB在该点处已满并沿着慢速路径行进,这在(3)中进行了解释。gc/shared/collectedHeap
代码为切入点,以分配慢速路径由于改变其用途。当TLAB被认为已满(因为分配已通过end
指针)时,代码进入collectedHeap
并尝试分配新的TLAB。此时,TLAB被设置回其原始大小并尝试分配。如果分配成功,则代码对分配进行采样,然后返回。如果没有,则意味着分配已到达TLAB的末尾,并且需要新的TLAB。代码路径继续正常分配新TLAB并确定该分配是否需要样本。如果对于TLAB来说分配被认为太大,系统也会对分配进行采样,从而覆盖TLAB和TLAB分配以进行采样。SampledObjectAlloc
事件注册了回调,则将触发该事件并获取采样分配。可以在libHeapMonitorTest.c
文件中找到示例实现,该文件用于JTreg测试。该JEP中提供的系统有多种替代方案。介绍已经介绍了两个:Flight Recorder提供了一个有趣的替代方案。该实现提供了若干优点。首先,JFR不允许设置采样大小或提供回调。接下来,当缓冲区耗尽时,JFR使用缓冲区系统会导致分配丢失。最后,JFR事件系统不提供跟踪垃圾收集对象的方法,这意味着无法使用它来提供有关实时和垃圾收集对象的信息。
另一种替代方法是使用ASM的字节码检测。它的开销使它变得过高而且不是一个可行的解决方案。
此JEP为JVMTI添加了一项新功能,JVMTI是各种开发和监视工具的重要API /框架。有了它,JVMTI代理可以使用低开销堆分析API以及JVMTI的其余功能,这为工具提供了极大的灵活性。例如,由代理决定是否需要在每个事件点收集堆栈跟踪。
JTreg框架中有16个测试用于此功能测试:打开/关闭多个线程,多个线程同时分配,测试数据是否以正确的间隔进行采样,以及收集的堆栈是否反映正确节目信息。
禁用该功能不会有性能损失或风险。未启用系统的用户将无法感知性能差异。
但是,启用该功能可能会导致性能/内存损失。在最初的原型实现中,开销很小(<2%)。这使用了一个更重量级的机制来修改JIT代码。在此处提供的最终版本中,系统会捎带TLAB代码,并且不应该经历该回归。
目前对Dacapo基准测试的评估将开销计入:
禁用此功能时为0%
以默认的512KB间隔启用该功能时为1%,但未执行回调操作(即,该SampledAllocEvent
方法为空但已注册到JVM)。
带有采样回调的3%开销,它执行一个简单的实现来存储数据(使用测试中的一个)
所有者 | 范雪蕾 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | security-libs / javax.net.ssl |
讨论 | openjdk dot java dot net的安全破折号 |
功夫 | XL |
持续时间 | XL |
评论人 | Andrew Gross,Brian Goetz,Sean Mullan |
受认可 | Brian Goetz |
创建 | 2015/12/12 05:26 |
更新 | 2018/09/17 19:03 |
问题 | 8145252 |
实现传输层安全性(TLS)协议RFC 8446的 1.3版。
支持数据报传输层安全性(DTLS)协议的1.3版不是目标。支持TLS 1.3的每个功能也不是目标; 有关要实现的内容的更多详细信息,请参阅“说明”部分。
TLS 1.3是TLS协议的重大改进,与以前的版本相比,它提供了显着的安全性和性能改进。其他供应商的几个早期实现已经可用。我们需要支持TLS 1.3以保持竞争力并与最新标准保持同步。
TLS 1.3是一个新的TLS版本,它取代并废弃了以前版本的TLS,包括版本1.2(RFC 5246)。它还废弃或更改其他TLS功能,例如OCSP装订扩展(RFC 6066,RFC 6961),以及会话散列和扩展主密钥扩展(RFC 7627)。
JDK中的Java安全套接字扩展(JSSE)提供SSL,TLS和DTLS协议的框架和Java实现。目前,JSSE API和JDK实现支持SSL 3.0,TLS 1.0,TLS 1.1,TLS 1.2,DTLS 1.0和DTLS 1.2。
此JEP的主要目标是实现最小的可互操作且兼容的TLS 1.3。最小的实现应该支持:
最小化实现不需要新的公共API。需要以下新标准算法名称:
TLSv1.3
javax.net.ssl.SSLContext
算法名称: TLSv1.3
TLS_AES_128_GCM_SHA256
,TLS_AES_256_GCM_SHA384
。此外,KRB5密码套件将从JDK中删除,因为它们不再被认为是安全的。
与此JEP并行,我们将为以下可选的TLS 1.3功能开发加密算法支持:
如果时间允许,这些功能可能包含在本JEP中; 否则它们将成为目标并作为单独的功能集成。
以下重要功能将不会作为此JEP的一部分实现:
TLS 1.3与以前的版本不直接兼容。尽管可以使用向后兼容模式实现TLS 1.3,但使用此模式时存在多种兼容性风险:
TLS 1.3使用半关闭策略,而TLS 1.2和先前版本使用双工关闭策略。对于依赖于双工关闭策略的应用程序,升级到TLS 1.3时可能存在兼容性问题。
该signature_algorithms_cert
扩展需要预先定义的签名算法用于证书的认证。然而,在实践中,应用程序可以使用不支持的签名算法。
TLS 1.3不支持DSA签名算法。如果服务器配置为仅使用DSA证书,则无法升级到TLS 1.3。
TLS 1.3支持的密码套件与TLS 1.2和早期版本不同。如果应用程序硬编码不再受支持的密码套件,则可能无法在不修改应用程序代码的情况下使用TLS 1.3。
为了最大限度地降低兼容性风险,默认情况下,此TLS 1.3实现将实现并启用向后兼容模式。应用程序可以关闭向后兼容模式,并根据需要打开或关闭TLS 1.3。
将开发或增强测试以验证以下一般要求:
互操作性测试需要支持RFC的第三方TLS 1.3实现。
TLS 1.3需要支持RSASSA-PSS签名算法(8146293)。
作者 | Per Liden,Stefan Karlsson |
所有者 | 佩里登 |
类型 | 特征 |
范围 | 履行 |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 热点/ gc |
讨论 | hotspot dash gc dash dev at openjdk dot java dot net |
功夫 | 大号 |
持续时间 | 大号 |
要看 | JEP 312:线程局部握手 |
JEP 304:垃圾收集器接口 | |
评论人 | Mikael Vidstedt,Stefan Karlsson |
受认可 | Mikael Vidstedt |
创建 | 2018/02/13 09:58 |
更新 | 2018/11/30 16:31 |
问题 | 8197831 |
Z垃圾收集器,也称为ZGC,是一个可扩展的低延迟垃圾收集器。
我们有雄心壮志为大量相关工作负载实现这些目标。与此同时,我们要承认,我们并未将这些目标视为对每种可能的工作量的硬性要求。
为Linux / x64以外的平台提供工作实现不是目标。如果有足够的需求,可以在以后添加对其他平台的支持。
垃圾收集是Java的主要优势之一。但是,当垃圾收集暂停时间过长时,它们会开始对应用程序响应时间产生负面影响。通过消除或大幅缩短GC暂停的长度,我们将使Java成为更广泛应用程序的更具吸引力的平台。
此外,现代系统中可用的内存量不断增长。用户和应用程序开发人员希望JVM能够以高效的方式充分利用此内存,并且无需长时间的GC暂停时间。
一目了然,ZGC是一个并发的,单代的,基于区域的,NUMA感知的压缩收集器。Stop-the-world阶段仅限于根扫描,因此GC暂停时间不会随堆或活动集的大小而增加。
ZGC的核心设计原则/选择是将负载障碍与彩色对象指针(即彩色哎呀)结合使用。这使得ZGC能够在Java应用程序线程运行时执行并发操作,例如对象重定位。从Java线程的角度来看,在Java对象中加载引用字段的行为受到加载障碍的影响。除了对象地址之外,有色对象指针还包含加载屏障使用的信息,以确定在允许Java线程使用指针之前是否需要采取某些操作。例如,对象可能已被重新定位,在这种情况下,加载屏障将检测情况并采取适当的操作。
与替代技术相比,我们认为彩色指针方案提供了一些非常有吸引力的特性。特别是:
它允许我们在重定位/压缩阶段回收和重用内存,然后修复指向回收/重用区域的指针。这有助于降低一般堆开销。这也意味着不需要实现单独的mark-compact算法来处理完整的GC。
它允许我们拥有相对较少且简单的GC障碍。这有助于降低运行时开销。这也意味着在我们的解释器和JIT编译器中实现,优化和维护GC屏障代码更容易。
我们目前在彩色指针中存储标记和重定位相关信息。但是,这种方案的通用性使我们能够存储任何类型的信息(只要我们可以将它放入指针中),并让负载屏障根据该信息采取任何想要的操作。我们相信这将为未来的许多功能奠定基础。举一个例子,在异构内存环境中,这可以用于跟踪堆访问模式,以指导GC重定位决策,将很少使用的对象移动到冷存储。
使用SPECjbb®2015[1]进行了定期性能测量。从吞吐量和延迟的角度来看,性能看起来都很好。下面是使用128G堆的复合模式下比较ZGC和G1的典型基准分数(百分比,相对于ZGC的max-jOPS标准化)。
(越高越好)
ZGC max-jOPS: 100% critical-jOPS: 76.1% G1 max-jOPS: 91.2% critical-jOPS: 54.7%
以下是来自同一基准的典型GC暂停时间。ZGC设法远远低于10毫米的目标。请注意,确切的数字可能会有所不同(向上和向下,但不会显着),具体取决于所使用的确切机器和设置。
(越低越好)
ZGC avg: 1.091ms (+/-0.215ms) 95th percentile: 1.380ms 99th percentile: 1.512ms 99.9th percentile: 1.663ms 99.99th percentile: 1.681ms max: 1.681ms G1 avg: 156.806ms (+/-71.126ms) 95th percentile: 316.672ms 99th percentile: 428.095ms 99.9th percentile: 543.846ms 99.99th percentile: 543.846ms max: 543.846ms
还对各种其他SPEC®基准测试和内部工作负载进行了临时性能测量。通常,ZGC设法保持一位数毫秒的暂停时间。
[1]SPECjbb®2015是Standard Performance Evaluation Corporation(spec.org)的注册商标。实际结果未表示为合规,因为SUT可能不符合SPEC对一般可用性的要求。
ZGC的初始实验版本不支持类卸载。在ClassUnloading
和ClassUnloadingWithConcurrentMark
选项会默认被禁用。启用它们将不起作用。
此外,ZGC最初不会支持JVMCI(即Graal)。如果EnableJVMCI
启用该选项,将打印错误消息。
这些限制将在本项目的后期阶段解决。
按照惯例,构建系统默认禁用JVM中的实验性功能。因此,作为实验性功能的ZGC将不会出现在JDK构建中,除非在编译时使用configure选项明确启用--with-jvm-features=zgc
。
(ZGC将出现在Oracle生成的所有Linux / x64 JDK版本中)
JVM中的实验功能也需要在运行时明确解锁。要启用/使用ZGC,因此需要以下JVM选项:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
。
有关如何设置和调整ZGC的更多信息,请参阅ZGC项目Wiki。
一个明显的替代方案是向G1添加并发压缩功能。这种替代方案是广泛的原型,但最终被放弃了。我们发现将这种功能强加于代码库是不可行的,该代码库从未为此目的而设计,同时保留了G1的稳定性和其他良好的属性。
理论上的替代方案是以某种方式改进CMS。然而,有几个原因使得基于CMS算法的低延迟收集器既不具有吸引力也不可行。原因包括不支持压缩,未绑定的注释阶段,复杂的代码库以及已经弃用的事实(JEP 291)。
Shenandoah项目正在探索使用Brooks指针来实现并发操作(JEP 189)。
我们现有的大多数功能和压力测试都是收集器无关的,可以按原样重复使用。将添加针对特定于ZGC的属性和功能的附加测试。
所有者 | 吉姆拉斯基 |
类型 | 特征 |
范围 | JDK |
状态 | 关闭/交付 |
发布 | 11 |
零件 | core-libs / jdk.nashorn |
讨论 | jdk dash dev在openjdk dot java dot net |
功夫 | 小号 |
评论人 | Alex Buckley,Brian Goetz,Dalibor主题 |
受认可 | 马克莱因霍尔德 |
创建 | 2018/05/08 14:23 |
更新 | 2018/07/17 11:21 |
问题 | 8202786 |
弃用Nashorn JavaScript脚本引擎和API以及该jjs
工具,目的是在将来的版本中删除它们。
Nashorn JavaScript引擎首先通过JEP 174合并到JDK 8中, 作为Rhino脚本引擎的替代品。发布时,它是ECMAScript-262 5.1标准的完整实现。
随着ECMAScript语言构建的快速步伐以及API的调整和修改,我们发现Nashorn难以维护。
此弃用不会以任何方式影响javax.script
API。
两个JDK模块将被最终弃用,即注释为@Deprecated(forRemoval=true)
:
jdk.scripting.nashorn
- 包含jdk.nashorn.api.scripting
和jdk.nashorn.api.tree
包。
jdk.scripting.nashorn.shell
- 包含jjs
工具。跑步jjs
会显示警告:
警告:计划从未来的JDK版本中删除jjs工具。
将在未来的JDK功能版本中提交单独的JEP以实际删除类型和模块。
另一种选择是让一群可靠的开发人员表达出对Nashorn前进的明确愿望。如果在集成JEP之前发生这种情况,则可以撤销此JEP。如果在集成此JEP之后发生这种情况,但在删除Nashorn之前,则后续JEP可以恢复弃用。
删除Nashorn的风险是某些应用程序将不再运行,因为期望存在JavaScript。Nashorn使用的广度并不容易追踪。希望此JEP的反馈可以更好地了解Nashorn的实际使用情况。
作者 | Kumar Srinivasan |
所有者 | 亨利仁 |
类型 | 特征 |
范围 | SE |
状态 | 关闭/交付 |
发布 | 11 |
零件 | 工具 |
讨论 | jdk dash dev在openjdk dot java dot net |
功夫 | 小号 |
持续时间 | 小号 |
评论人 | 亚历克斯巴克利,约翰罗斯 |
受认可 | 约翰罗斯 |
创建 | 2018/04/04 17:11 |
更新 | 2018/10/12 00:50 |
问题 | 8200752 |
弃用pack200
和unpack200
工具以及Pack200
API java.util.jar
。
Pack200是JAR文件的压缩方案。它是由JSR 200在Java SE 5.0中引入的。其目标是“降低Java应用程序打包,传输和交付的磁盘和带宽要求”。开发人员使用一对工具- pack200
和unpack200
-来压缩和解压的JAR文件。包中提供了APIjava.util.jar
。
想要弃用(并最终删除)Pack200有三个原因:
从历史上看,JDK超过56k调制解调器的缓慢下载是Java采用的障碍。JDK功能的不断增长导致下载量大幅增加,进一步阻碍了采用。使用Pack200压缩JDK是一种缓解问题的方法。但是,时间已经过去了:下载速度有所提高,JDK 9为Java运行时(JEP 220)和用于构建运行时(JMOD)的模块引入了新的压缩方案。因此,JDK 9及更高版本不依赖Pack200; JDK 8是pack200
在构建时压缩的最后一个版本,unpack200
在安装时未压缩。总之,Pack200的主要消费者 - JDK本身 - 不再需要它。
除了JDK之外,使用Pack200压缩客户端应用程序,尤其是applet也很有吸引力。某些部署技术(如Oracle的浏览器插件)会自动解压缩applet JAR。但是,客户端应用程序的格局已发生变化,大多数浏览器都不再支持插件。因此,Pack200的一大类消费者 - 在浏览器中运行的applet - 不再是将Pack200包含在JDK中的驱动程序。
Pack200是一项复杂而精细的技术。的文件格式被紧密地结合到类文件格式和JAR文件格式,这两者在由JSR 200不可预见的方式已经进化(例如,JEP 309增加了一个新的常量存储库项的类文件格式的,并JEP 238将版本控制元数据添加到JAR文件格式。)JDK中的实现在Java和本机代码之间分配,这使得难以维护。API in java.util.jar.Pack200
不利于Java SE平台的模块化,导致在Java SE 9中删除了四种方法。总体而言,维护Pack200的成本非常高,并且超过了将其包含在Java SE和JDK中的好处。
java.base
模块中的三种类型将被最终弃用,即注释为@Deprecated(forRemoval=true)
:
java.util.jar.Pack200
java.util.jar.Pack200.Packer
java.util.jar.Pack200.Unpacker
jdk.pack
包含pack200
和unpack200
工具的模块也将被最终弃用。
运行pack200
或unpack200
将显示有关计划删除工具的警告。jar -c
使用子选项运行n
(以归档化存档)将显示有关计划删除子选项的警告。所有三个工具的文档都将指出弃用和计划删除。
将在未来的JDK功能版本中提交单独的JEP以实际删除类型和模块。
假设pack200
用于收缩应用程序JAR的开发人员将切换到该jlink
工具,以使用优化的外形创建特定于应用程序的运行时。请参阅工具文档和JEP 282。另一种选择可能是jpackager
工具(JEP草案)。
转载来源:http://openjdk.java.net/projects/jdk/11/,http://openjdk.java.net/jeps/181,http://openjdk.java.net/jeps/309,http://openjdk.java.net/jeps/315,http://openjdk.java.net/jeps/318,http://openjdk.java.net/jeps/320,http://openjdk.java.net/jeps/321,http://openjdk.java.net/jeps/323,http://openjdk.java.net/jeps/324,http://openjdk.java.net/jeps/327,http://openjdk.java.net/jeps/328,http://openjdk.java.net/jeps/329,http://openjdk.java.net/jeps/330,http://openjdk.java.net/jeps/331,http://openjdk.java.net/jeps/332,http://openjdk.java.net/jeps/333,http://openjdk.java.net/jeps/335,http://openjdk.java.net/jeps/336