JVM方法句柄

2023-10-27

JVM方法句柄

方法句柄是一个强类型的,能够被直接执行的引用。该引用可以指向常规的静态方法或者实例方法,也可以指向构造器或者字段。当指向字段时,方法句柄实则指向包含字段访问字节码的虚构方法,语义上等价于目标字段的 getter 或者 setter 方法

**方法句柄的类型(MethodType)**是由所指向方法的参数类型以及返回类型组成的。它是用来确认方法句柄是否适配的唯一关键。当使用方法句柄时,我们其实并不关心方法句柄所指向方法的类名或者方法名

**方法句柄的创建是通过 MethodHandles.Lookup 类来完成的。**它提供了多个 API,既可以使用反射 API 中的 Method 来查找,也可以根据类、方法名以及方法句柄类型来查找

**当使用后者这种查找方式时(方法句柄类型),**用户需要区分具体的调用类型,比如说对于用 invokestatic 调用的静态方法,我们需要使用 Lookup.findStatic 方法;对于用 invokevirutal 调用的实例方法,以及用 invokeinterface 调用的接口方法,我们需要使用 findVirtual 方法;对于用 invokespecial 调用的实例方法,我们则需要使用 findSpecial 方法。

具体操作

方法类型

一个 Java 方法可以视为由四个基本内容所构成:

  • 名称

  • 签名(包含返回类型)

  • 定义它的类

  • 实现方法的字节码

方法句柄首先需要的一个构建块就是表达方法签名的方式,以便于查找。在 Java 7 引入的 Method Handles API 中,**这个角色是由 java.lang.invoke.MethodType 类来完成的,它使用一个不可变的实例来代表签名。要获取 MethodType,**我们可以使用 methodType() 工厂方法。这是一个参数可变(variadic)的方法,以 class 对象作为参数。

第一个参数所使用的 class 对象,对应着签名的返回类型;剩余参数中所使用的 class 对象,对应着签名中方法参数的类型。例如:


//toString() 的签名 
MethodType mtToString = MethodType.methodType(String.class);

// setter 方法的签名 
MethodType mtSetter = MethodType.methodType(void.class, Object.class);

// Comparator 中 compare() 方法的签名 
MethodType mtStringComparator = MethodType.methodType(int.class, String.class)

**现在可以使用 MethodType,再组合方法名称以及定义方法的类来查找方法句柄。**要实现这一点,**我们需要调用静态的 MethodHandles.lookup() 方法。**这样的话,会给我们一个“查找上下文(lookup context)”,这个上下文基于当前正在执行的方法(也就是调用 lookup() 的方法)的访问权限。

**查找上下文对象有一些以“find”开头的方法,例如,findVirtual()、findConstructor()、findStatic() 等。这些方法将会返回实际的方法句柄,**需要注意的是,只有在创建查找上下文的方法能够访问(调用)被请求方法的情况下,才会返回句柄。这与反射不同,我们没有办法绕过访问控制。换句话说,方法句柄中并没有与 setAccessible() 对应的方法。

通过MethodHandle进行方法调用一般需要以下几步:

  • (1)创建MethodType对象,指定方法的签名;

  • (2)在MethodHandles.Lookup中查找类型为MethodType的MethodHandle;

  • (3)传入方法参数并调用MethodHandle.invoke或者MethodHandle.invokeExact方法。

MethodType

可以通过MethodHandle类的type方法查看其类型,返回值是MethodType类的对象。也可以在得到MethodType对象之后,调用MethodHandle.asType(mt)方法适配得到MethodHandle对象。可以通过调用MethodType的静态方法创建MethodType实例,有三种创建方式:

  • (1)methodType及其重载方法:需要指定返回值类型以及0到多个参数;

  • (2)genericMethodType:需要指定参数的个数,类型都为Object;

  • (3)fromMethodDescriptorString:通过方法描述来创建。

Lookup

MethodHandle.Lookup相当于MethodHandle工厂类,通过findxxx方法可以得到相应的MethodHandle

几个 MethodHandle 方法与字节码的对应:

  • findStatic
    对应字节码:invokestatic
    调用静态方法

  • findSpecial
    对应字节码:invokespecial
    调用实例构造方法,私有方法,父类方法

  • findVirtual
    对应字节码:invokevirtual
    调用所有的虚方法

  • findVirtual
    对应字节码:invokeinterface
    调用接口方法,会在运行时再确定一个实现此接口的对象

invoke

在得到MethodHandle后就可以进行方法调用了,有三种调用形式:

  • (1)invokeExact:调用此方法与直接调用底层方法一样,需要做到参数类型精确匹配;

  • (2)invoke:参数类型松散匹配,通过asType自动适配;

  • (3)invokeWithArguments:直接通过方法参数来调用。其实现是先通genericMethodType方法得到MethodType,再通过MethodHandle的asType转换后得到一个新的MethodHandle,最后通过新MethodHandle的invokeExact方法来完成调用。

官方文档例子

public class examples {

    public static void main(String[] args) throws Throwable {


        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");
        System.out.println(s);
        System.out.println("-----------------------------");



// weakly typed invocation (using MHs.invoke)
        s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
//        assertEquals(s, "savvy");
        System.out.println(s);
        System.out.println("-----------------------------");



// 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"));
        System.out.println(x);
        System.out.println("-----------------------------");


// 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));
        System.out.println(x);
        System.out.println("-----------------------------");



// 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

    }
}


MethodHandle 与 Method 区别

  • MethodHandle 在模拟 字节码 层次的方法调用,因而可适用于所有 JVM 语言 ;

  • Reflection 在模拟 Java 层次的方法调用,仅可适用于 Java。

  • MethodHandle 可进行 JVM 的内联优化,Reflection 屏蔽了 JVM ,所以完全没有相应的优化。

  • MethodHandle 从 JVM 层次支持调用,只需要包含方法必要的信息,所以说是轻量级的,而 Reflection 是 Java Api 层次的反射调用,包含了方法的签名、描述符以及方法属性表中各种属性的Java端表示方式,还包含有执行权限等的运行期信息,所以说是重量级的。

  • MethodHandle 方法调用需要考虑到 字节码,而 Reflection 则不用考虑这些。

参考文章

https://www.infoq.cn/article/Invokedynamic-Javas-secret-weapon

https://segmentfault.com/a/1190000017208820

https://www.zhihu.com/question/40427344/answer/252825611

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM方法句柄 的相关文章

随机推荐

  • BOSE在上海发布6款音频新品;轩尼诗全球首家概念酒吧在上海开幕

    今日看点 2020 BOSE音频新品发布会在上海举行 此次发布会总共发布了6款产品 包括Bose 消噪耳塞 Bose无线耳塞 Bose 智能音频眼镜 猫眼款和运动款 Bose 遮噪睡眠耳塞 II QC 35 II GAMING HEADSE
  • Shell基础:批量运行程序

    创建可执行程序 文件名 hello c include
  • 将eclipse项目导入idea

    首相我们需要明白idea的两个概念 在idea中project相当于eclipse的workspace 在idea中project相当于eclipse的project 所有首先我们需要新建一个在idea中创建一个project 点击菜单fi
  • 标签
  • 引起的缩进问题 list-style: none outside none;
  • 一般网站css都有相应的初始化部分 最近遇到一个棘手问题 li 标签里面的东西 在firefox下正常 而在ie6全部被缩进 而初始化的部分css也有 list style none 并且这个li没有相应的id或者class 那么是被什么控
  • Python判断指定日期是不是中国工作日/节假日

    判断一个日期是否为工作日 节假日 有一个现成的库函数 chinesecalendar chinesecalendar PyPI 1 安装与升级 1 1 安装 pip3 install chinesecalendar 1 2 升级 pip i
  • 11.全连接卷积神经网络 FCN

    视频 48 全连接卷积神经网络 FCN 动手学深度学习v2 哔哩哔哩 bilibili 书籍 13 11 全卷积网络 动手学深度学习 2 0 0 beta0 documentation d2l ai PPT part 2 16 pdf d2
  • 程序员必知的7种软件架构模式

    架构模式是对给定上下文的软件架构中常见问题的一种通用的可复用的解决方案 一种模式就是特定上下文的问题的一种解决方案 然而 很多开发者至今还对各种软件架构模式之间的差别搞不清 甚至对其所知甚少 大体上 主要有下面这7种架构模式 分层架构 多层
  • 2017年蓝桥杯B组C/C++省赛-K倍区间

    题目 题解 思维 暴力的话是会超时的 但也可以骗点分 采用差分数组暴力 讲一下AC思路 统计出来每个前缀和取模 k k k后结果的个数 比如 c n t
  • 优酷iOS插件化页面架构方法

    Python实战社群 Java实战社群 长按识别下方二维码 按需求添加 扫码关注添加客服 进Python社群 扫码关注添加客服 进Java社群 作者 iOS一叶 来源 掘金 点击阅读原文查看作者更多文章 一 前言 随着业务不停地迭代 优酷
  • openwrt固件格式

    标准格式 factory img bin 从非openwrt系统升级到openwrt系统时 或者工厂烧写时使用的包 如果你的路由器只有sysupgrade镜像 路由器已经运行某种OpenWrt分支 它可以理解系统格式 或通过OEM UI的w
  • 如何批量修改文件名

    步骤 1 2 3 4
  • 测试域: 流量回放-介绍篇

    建设背景 测试人员回归耗时长 成本大 公司很多测试都进行手工测试 在集成测试中需要耗费一周时间进行全量测试 在各个环境 用户测试环境和预发布环境 回归测试时需要耗费三天左右 加上编写测试用例时间 理解需求时间等其他 测试人员时间紧张 无法适
  • 调用股票交易软件接口有什么步骤?

    调用股票交易软件接口有什么步骤 第一步要做的就是定位出现问题的股票交易软件接口 并确定问题的具体所在 这样才能对症下药 第二步就是进行状态码的检测 股票交易软件接口会返回一些HTTP状态码 这些状态码代表着不同的含义 144 登出交易账户
  • 数据库系统原理课程总结5——数据库系统制作(python+SQL+HTML)

    实验任务要求 结合自己所选的应用案例 至少完成一个简单案例 Web页面的操作应包括增 删 改 查 查询结果以表格或表单形式展现 整个系统架构至少应包括前端 Web服务器 应用服务器 数据库服务器 Web服务器和应用服务器可以合在一起 也可以
  • 一文了解大厂的DDD领域驱动设计

    1 什么是DDD DDD名为 Domain Driven Design 领域驱动设计 简称 DDD 概念来源于2004年著名建模专家eric evans发表的他最具影响力的书籍 2 DDD与我们的传统开发又有什么区别和优势 有过工作的朋友都
  • EDUCODER---计算机硬件基础---计算机系统测试 5.1&6.1&7.1&9.1 合集

    由于时间关系 在这里我将全部的测试题答案全部发出 5 1 1 5 A D D A D 6 10 C D A B A 6 1 1 5 B A B A A 6 10 A C C A C 7 1 1 5 A C D A B 6 10 B B A
  • GitHub 优秀的 Android 开源项目和框架

    GitHub 优秀的 Android 开源项目 淘宝技术牛p博客整理开发中最常用的GitHub上 优秀的 Android 开源项目整理 精品 博客分类 Android 开源集合 本文章已收录于 Git 原文地址为http www trine
  • ubuntu中执行脚本出现no such file or directory

    问题 在ubuntu下执行 sh文件大多数情况下只需要注意给脚本文件添加可执行权限就可以了 但是有些情况下会出现这种问题 从上图可以看到 文件夹中的几个脚本文件的权限都达到了 777 按理说任何用户都可以执行这些脚本 但是执行其中任意的就出
  • 【最详细附安装包】Visual Studio2022安装教程

    软件下载 软件 Visual Studio 版本 2022 语言 简体中文 大小 4 11M 安装环境 Win11 Win10 Win8 Win7 硬件要求 CPU 2 0GHz 内存 4G 或更高 下载通道 百度网盘丨下载链接 https
  • JVM方法句柄

    JVM方法句柄 方法句柄是一个强类型的 能够被直接执行的引用 该引用可以指向常规的静态方法或者实例方法 也可以指向构造器或者字段 当指向字段时 方法句柄实则指向包含字段访问字节码的虚构方法 语义上等价于目标字段的 getter 或者 set