JDK11中增加了一个常量池类型:CONSTANT_Dynamic

Posted ImportSource

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK11中增加了一个常量池类型:CONSTANT_Dynamic相关的知识,希望对你有一定的参考价值。

有关常量池


有关class file 的内容,这里不再赘述。你可以参阅此文:。


大体格式长下面这样:


ClassFile {

    u4             magic;

    u2             minor_version;

    u2             major_version;

    u2             constant_pool_count;

    cp_info        constant_pool[constant_pool_count-1];

    u2             access_flags;

    u2             this_class;

    u2             super_class;

    u2             interfaces_count;

    u2             interfaces[interfaces_count];

    u2             fields_count;

    field_info     fields[fields_count];

    u2             methods_count;

    method_info    methods[methods_count];

    u2             attributes_count;

    attribute_info attributes[attributes_count];

}


你可以看到其中一个属性是cp_info。在早前的一些代码中,我们可以发现cp_info是一个数组。这个数组中就是常量池。里边放着各种常量。如下:



cp_info的格式如下:


struct cp_info

{

    u1 tag;

    u1* info;

};


有一个tag,和一个info。其中tag就表示不同的常量类型。info存储了常量的值或者引用。


tag是常量类型。那么在jvm 规范中定了很多的常量类型:


JDK11中增加了一个常量池类型:CONSTANT_Dynamic

此图是从官方的https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.3.2截取的。


ps:可以看出是截止到jvm规范4.3.2的常量类型。可以发现在java7的时候加入了有关动态语言的常量类型;在java9的时候又加入了有关模块化的两个常量类型。

在java8的时候和在java10的时候并没有更新jvm规范。


上面我们展示了cp_info的基本格式,但具体到每个类型的常量则会转换成具体的常量存储格式,比如如果是

CONSTANT_Integer,则会转成如下格式:


struct CONSTANT_Integer_info {

    u1 tag;

    u4 bytes;

};


等等,每个具体的tag(常量类型)都会有具体的存储格式。这里就不一一赘述。


有关invokedynamic


我们知道在java7的时候加入了动态语言的支持。上面也说到了,虚拟机规范也添加了支持动态语言的三个常量类型:


JDK11中增加了一个常量池类型:CONSTANT_Dynamic


这里还是简单的介绍一下invokedynamic吧。


invokedynamic是一个字节码指令,也就是bytecode instruction,通过动态方法的调用来实现动态语言。比如常见的Grovvy。


有关动态语言:动态语言的一个突出特点就是它的类型检查是在运行时才进行,叫做dynamic typing。


这里贴上一段代码你可以简单体验一下java7中新增的有关动态语言的API:


import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MHD
{
  public static void main(String[] args) throws Throwable
  {
     MethodHandles.Lookup lookup = MethodHandles.lookup();
     MethodHandle mh = lookup.findStatic(MHD.class, "hello",
                                         MethodType.methodType(void.class));
     mh.invokeExact();
  }

  static void hello()
  {
     System.out.println("hello");
  }
}


上面的代码中我们通过MethodHandles类获取到Lookup对象,然后通过Lookup查找到MHD类中的静态方法hello(),然后我们设置方法返回类型为void。


查找到这个这个方法以后,返回的是一个MethodHandle对象,此对象封装了hello方法的全部信息,然后我们调用invokeExact方法来执行hello方法,然后打印出hello。


JDK11中增加了一个常量池类型:CONSTANT_Dynamic


你也发现了,这里我们又看到了MethodHandle和MethodType了。在java7中也对虚拟机规范添加了这两个对应的常量类型。



有关JDK11要新增的CONSTANT_Dynamic


根据JDK11的road map,JDK11将会在2018年9月份就GA了。



其中要发布的新功能中有一项就是要新增一个常量类型:CONSTANT_Dynamic。


为什么要新增这个常量类型?


JEP309 Motivation原文:


Section 4.4 of the Java Virtual Machine Specification describes the format of the constant pool. Adding new constant-pool forms, such as the support for MethodHandle and MethodType introduced in Java 7, is a significant endeavor, and sends ripples through the ecosystem, as it affects all code that parses or interprets class files. This presents a very high bar to creating new constant-pool forms.


With invokedynamic, the value of storing complex data in the constant pool is multiplied, since the static argument list for an invokedynamic bootstrap is a sequence of constants. Designers of invokedynamic protocols (such as the LambdaMetafactory added in Java 8) routinely struggle with the need to encode behavior in terms of the existing constant set—which in turn necessitates additional error-prone validation and extraction logic in the bootstrap itself. Richer, more flexible, more highly-typed constants remove friction from the development of invokedynamic protocols, which in turn facilitates the movement of complex logic from run time to linkage time, improving program performance and simplifying compiler logic.


大体上说就是说虚拟机规范在Java7的时候新增了MethodHandle和MethodType两个常量类型。


有了invokedynamic以后,由于invokedynamic bootstrap的静态参数列表是一系列常量,也就是好多个常量。在常量池中存储复杂数据的情况就变得比较普遍。invokedynamic协议的设计者(例如在Java 8中添加的LambdaMetafactory)经常会根据现有的常量集来对行为进行转换,而这又需要在引导(bootstrap)程序中添加一些防止出错的验证。


所以就需要一个更丰富、更高级的常量类型,这样可以解决很多开发invokedynamic协议的麻烦,从而还能改善程序性能和简化编译器逻辑。


为此新增了CONSTANT_Dynamic常量类型。


JVMS也添加了CONSTANT_Dynamic


而且我们注意到了最新的虚拟机规范的草稿已经添加了这一个常量类型。


最新的常量类型表:



新增(修改)的两个常量类型格式如下:


CONSTANT_DynamicCallSite_info {

   u1 tag;

   u2 bootstrap_method_attr_index;

   u2 name_and_type_index;

}


CONSTANT_Dynamic_info {

   u1 tag;

   u2 bootstrap_method_attr_index;

   u2 name_and_type_index;

}


具体细节你可以翻阅虚拟机规范的最新定义:http://cr.openjdk.java.net/~jrose/jvm/constant-dynamic-jrose.html

以上是关于JDK11中增加了一个常量池类型:CONSTANT_Dynamic的主要内容,如果未能解决你的问题,请参考以下文章

面试官:说说 String.intern() 和常量池?不同 JDK 版本有什么区别?

面试官:说说 String.intern() 和常量池?不同 JDK 版本有什么区别?

类文件结构

Class文件-常量池

0007JDK源码分析之揭秘整数常量池实现机制

Jvm(35.1),class文件结构----常量池(上)