编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • C语言入门精通

  • Cpp入门到精通

  • Java入门精通

    • README
    • 入门教程

    • 综合案例

    • 专栏博客

      • README
      • JVM内存模型与对象
      • 类加载与双亲委派
      • 垃圾回收与GC调优
      • 异常体系与JVM机制
      • 字节码指令集javap实战
      • JIT编译与去优化机制
      • JVM性能诊断工具链
      • OOM八大现场全景剖析
      • JVM参数调优全景图
      • GraalVM与AOT编译原理
      • HashMap底层哈希设计
      • String不可变与常量池
      • ArrayList与LinkedList源码
      • ConcurrentHashMap并发
      • TreeMap与红黑树原理
      • LinkedHashMap与LRU实现
      • Java数字类型原理
      • Object通用方法的契约
      • 泛型擦除与类型系统
      • 枚举原理与最佳实践
      • 注解原理与编译期处理
      • Lambda与引用底层原理
      • Stream原理与流水线设计
      • Optional设计原理
      • Record密封类与模式
      • 反射机制与动态代理
      • MethodHandle与VarHandle
        • 1. 案例引入
          • 1.1 反射热路径的性能毒瘤
          • 1.2 Unsafe 退场的恐慌
          • 1.3 我们要回答什么
        • 2. java.lang.invoke 全景
          • 2.1 包结构与七大主角
          • 2.2 反射的进化时间线
          • 2.3 与 invokedynamic 的同源关系
        • 3. MethodHandle 句柄抽象
          • 3.1 MethodType 类型签名
          • 3.2 Lookup 五大解析器
          • 3.3 invoke vs invokeExact
          • 3.4 句柄变换 asType/bindTo/insertArguments
        • 4. invokedynamic 与 LambdaMetafactory
          • 4.1 CallSite 三种形态
          • 4.2 引导方法 BootstrapMethod
          • 4.3 LambdaMetafactory 落地链路
          • 4.4 SwitchBootstraps 串讲
        • 5. VarHandle 内存语义革命
          • 5.1 为什么 Unsafe 必须退场
          • 5.2 四级访问模式
          • 5.3 CAS 与 weakCompareAndSet
          • 5.4 内存屏障 fence API
        • 6. 性能实证
          • 6.1 JMH 三方对比
          • 6.2 invokeExact 才是性能之王
          • 6.3 字段访问的性能阶梯
          • 6.4 性能差异的根因
        • 7. 实战案例
          • 7.1 高性能动态字段读取器
          • 7.2 手撕 Switch 模式底层
          • 7.3 无锁原子计数器
        • 8. 使用边界与陷阱
          • 8.1 invokeExact 类型严格匹配
          • 8.2 Lookup 模块封装
          • 8.3 VarHandle 必须 static final
          • 8.4 反模式清单
        • 9. 综合案例串讲
          • 9.1 双案例真相揭晓
          • 9.2 一次句柄调用的旅行
          • 9.3 设计哲学回扣
          • 9.4 速查表
      • 三大字节码框架对比
      • JavaAgent与Instrumentation机制
      • AOP三种实现路线对比
      • synchronized与锁升级
      • volatile与JMM内存模型
      • 线程池核心源码设计
      • Thread线程生命周期
      • AQS同步框架源码
      • 并发锁三剑客
      • CAS和Atomic深入分析
      • 五大同步器对比
      • CompletableFuture异步
      • IO模型演进BIO到AIO
      • ByteBuffer与堆外内存
      • 序列化原理与替代方案
      • 文件IO与NIO.2
      • 面向对象的真意
      • JDK设计模式上
      • JDK设计模式下
      • SPI与模块化设计
  • Go入门到精通

  • JavaScript入门

  • CodeX
  • Java入门精通
  • 专栏博客
杨充
2026-06-02
目录

MethodHandle与VarHandle

# 27.MethodHandle与VarHandle

# 目录介绍

  • 1. 案例引入
    • 1.1 反射热路径的性能毒瘤
    • 1.2 Unsafe 退场的恐慌
    • 1.3 我们要回答什么
  • 2. java.lang.invoke 全景
    • 2.1 包结构与七大主角
    • 2.2 反射的进化时间线
    • 2.3 与 invokedynamic 的同源关系
  • 3. MethodHandle 句柄抽象
    • 3.1 MethodType 类型签名
    • 3.2 Lookup 五大解析器
    • 3.3 invoke vs invokeExact
    • 3.4 句柄变换 asType/bindTo/insertArguments
  • 4. invokedynamic 与 LambdaMetafactory
    • 4.1 CallSite 三种形态
    • 4.2 引导方法 BootstrapMethod
    • 4.3 LambdaMetafactory 落地链路
    • 4.4 SwitchBootstraps 串讲
  • 5. VarHandle 内存语义革命
    • 5.1 为什么 Unsafe 必须退场
    • 5.2 四级访问模式
    • 5.3 CAS 与 weakCompareAndSet
    • 5.4 内存屏障 fence API
  • 6. 性能实证
    • 6.1 JMH 三方对比
    • 6.2 invokeExact 才是性能之王
    • 6.3 字段访问的性能阶梯
    • 6.4 性能差异的根因
  • 7. 实战案例
    • 7.1 高性能动态字段读取器
    • 7.2 手撕 Switch 模式底层
    • 7.3 无锁原子计数器
  • 8. 使用边界与陷阱
    • 8.1 invokeExact 类型严格匹配
    • 8.2 Lookup 模块封装
    • 8.3 VarHandle 必须 static final
    • 8.4 反模式清单
  • 9. 综合案例串讲
    • 9.1 双案例真相揭晓
    • 9.2 一次句柄调用的旅行
    • 9.3 设计哲学回扣
    • 9.4 速查表

# 1. 案例引入

# 1.1 反射热路径的性能毒瘤

某 RPC 框架通过反射做参数填充,QPS 压测时火焰图惊现 Method.invoke 占据 35% CPU:

public class FastInvoker {
    
    // 启动时缓存 Method 对象
    private final Method method;
    
    public FastInvoker(Class<?> clazz, String name) throws Exception {
        this.method = clazz.getMethod(name, String.class);
        this.method.setAccessible(true);   // ★ 关闭访问检查
    }
    
    public Object invoke(Object target, String arg) throws Exception {
        // ★ 热路径:每秒 100w 次调用
        return method.invoke(target, arg);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

火焰图证据(10 万次调用,99 分位耗时):

直接调用       :    8 ns/op    ( 1×, baseline)
缓存的反射     :  165 ns/op    (20× 慢)
未缓存反射     : 1240 ns/op    (155× 慢)
1
2
3

疑惑:

  • Method 对象都已经缓存了,为什么还是这么慢?
  • 反射调用时 JVM 内部到底做了什么?
  • 有没有"反射级灵活 + 直接调用级性能"的中间方案?

# 1.2 Unsafe 退场的恐慌

某高性能并发库依赖 sun.misc.Unsafe 做 CAS 字段更新:

public class LongAdderCell {
    
    private static final Unsafe UNSAFE;
    private static final long VALUE_OFFSET;
    
    static {
        try {
            // ★ 反射拿 Unsafe(JDK 17+ 强烈警告,JDK 24 可能彻底移除)
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            UNSAFE = (Unsafe) f.get(null);
            VALUE_OFFSET = UNSAFE.objectFieldOffset(LongAdderCell.class.getDeclaredField("value"));
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    private volatile long value;
    
    public boolean cas(long expect, long update) {
        return UNSAFE.compareAndSwapLong(this, VALUE_OFFSET, expect, update);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

JDK 17 启动告警:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by ... to field jdk.internal.misc.Unsafe.theUnsafe
WARNING: Please consider reporting this to the maintainers of ...
WARNING: All illegal reflective access operations will be denied in a future release
1
2
3
4

疑惑:

  • Unsafe 真的会被移除吗?JDK 团队推荐用什么替代?
  • "替代品"的内存语义和 Unsafe 等价吗?
  • volatile 写、CAS、内存屏障,都怎么落到新 API 上?

# 1.3 我们要回答什么

第 31 篇是卷四第 2 篇,紧承 07 篇的反射机制(卷四第 1 篇),把"反射的现代继任者"完整讲透:

java.lang.invoke 双子星:

MethodHandle  → 反射调用的现代替代(方法/构造器层面)
                ↓
                与 invokedynamic / LambdaMetafactory / SwitchBootstraps 同源
                
VarHandle     → Unsafe 的官方继任者(字段/数组层面)
                ↓
                提供精细的内存语义控制(plain/opaque/acquire-release/volatile)
                
        共同特征:JIT 友好 + 类型严格 + 性能接近直接调用
1
2
3
4
5
6
7
8
9
10
11

带着这个目标回答 7 个核心问题:

追问 ①:MethodHandle 比反射快多少?快在哪?           → 第3.3、§6.1、§6.4
追问 ②:MethodType 是什么?为什么 invokeExact 这么严格? → 第3.1、§3.3
追问 ③:MethodHandle 与 invokedynamic 是什么关系?      → 第4章
追问 ④:VarHandle 凭什么替代 Unsafe?                  → 第5章
追问 ⑤:四级内存语义到底有什么区别?                   → 第5.2
追问 ⑥:JMH 实测三种调用方式的真实差距?               → 第6章
追问 ⑦:动态字段读取器/SwitchBootstraps/无锁队列怎么写? → 第7章
1
2
3
4
5
6
7

本篇路线:

java.lang.invoke 全景 (第2章)        ─── 总览
       ↓
MethodHandle 句柄抽象 (第3章)         ←—— 方法层
       ↓
invokedynamic / LambdaMetafactory (第4章)  ←—— 与语言特性的桥
       ↓
VarHandle 内存语义革命 (第5章)         ←—— 字段层
       ↓
性能实证 (第6章)                      ←—— JMH 数据
       ↓
实战案例 (第7章)                      ←—— 落地
       ↓
使用边界与陷阱 (第8章)
       ↓
综合案例串讲 (第9章)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 2. java.lang.invoke 全景

# 2.1 包结构与七大主角

java.lang.invoke 包诞生于 JDK 7(JSR 292),是为支持动态语言(JRuby/Groovy/Scala)而引入的"运行时句柄"基础设施:

java.lang.invoke 包结构:

MethodHandle              方法句柄(核心)
MethodHandles             工具集合(静态方法工厂)
MethodHandles.Lookup      查找器("权限令牌")
MethodType                方法类型签名(不可变)
CallSite                  调用点(invokedynamic 的链接对象)
  ├─ ConstantCallSite     常量调用点(链接后不可改)
  ├─ MutableCallSite      可变调用点(target 可改)
  └─ VolatileCallSite     volatile 调用点(target 改变立即可见)
VarHandle                 变量句柄(JDK 9 新增,替代 Unsafe)
LambdaMetafactory         Lambda 引导工厂
StringConcatFactory       字符串拼接引导工厂(JDK 9)
SwitchBootstraps          switch 模式引导工厂(JDK 21)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.2 反射的进化时间线

JDK 1.1  (1997)  java.lang.reflect              传统反射
JDK 7    (2011)  MethodHandle / invokedynamic   JSR 292 「Da Vinci Machine」
JDK 8    (2014)  LambdaMetafactory              Lambda 落地
JDK 9    (2017)  VarHandle / StringConcatFactory  Unsafe 替代 + 拼接优化
JDK 11   (2018)  Nest-based Access              嵌套类访问优化
JDK 14   (2020)  ConstantBootstraps             常量动态生成
JDK 21   (2023)  SwitchBootstraps               switch 模式底层 ★
1
2
3
4
5
6
7

# 2.3 与 invokedynamic 的同源关系

invokedynamic(简称 indy)是 JDK 7 引入的第 5 种方法调用字节码指令——前 4 种是 invokestatic / invokespecial / invokevirtual / invokeinterface(详见 13 篇)。

indy 指令的核心思想:

第一次执行:
  调用 BootstrapMethod(引导方法),返回一个 CallSite
  CallSite 内含 MethodHandle(真正执行的目标)
  把 CallSite 与 indy 调用点绑定

后续执行:
  直接通过 CallSite.target(MethodHandle)调用
  绕过 BootstrapMethod,性能与直接调用接近

→ MethodHandle 不是孤立 API,它是 indy 的"调用目标"
→ Lambda、Switch 模式、字符串拼接,全部基于这个机制
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3. MethodHandle 句柄抽象

# 3.1 MethodType 类型签名

MethodType 描述方法的"形状"——返回值类型 + 所有参数类型:

// String foo(int, double)  → MethodType.methodType(String.class, int.class, double.class)
MethodType mt = MethodType.methodType(String.class, int.class, double.class);
System.out.println(mt);    // (int,double)String

// 无参方法 void bar()
MethodType mt2 = MethodType.methodType(void.class);    // ()void

// 数组参数 byte[] baz(byte[])
MethodType mt3 = MethodType.methodType(byte[].class, byte[].class);
1
2
3
4
5
6
7
8
9

特点:

  • 不可变(immutable):所有变换返回新实例
  • 驻留(interned):相同签名的 MethodType 是同一对象(== 比较成立)
  • JVM 内部表示:底层与 Class[] 平行,常量池可直接引用

# 3.2 Lookup 五大解析器

MethodHandles.Lookup 是"权限令牌"——它持有调用方的访问权限,决定能查找哪些方法:

// 在调用方所在的类中调用,获取该类视角的 Lookup
MethodHandles.Lookup lookup = MethodHandles.lookup();
1
2

五大解析方法:

方法 等价于 用途
findStatic(class, name, type) invokestatic 静态方法
findVirtual(class, name, type) invokevirtual 实例方法(虚分派)
findSpecial(class, name, type, caller) invokespecial super 调用 / private
findConstructor(class, type) invokespecial <init> 构造器
findGetter / findSetter getfield/putfield 字段访问

示例:

public class Demo {
    
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        
        // ① findStatic:String.valueOf(int)
        MethodType mt1 = MethodType.methodType(String.class, int.class);
        MethodHandle mh1 = lookup.findStatic(String.class, "valueOf", mt1);
        String s1 = (String) mh1.invoke(42);    // "42"
        
        // ② findVirtual:String.length()
        MethodType mt2 = MethodType.methodType(int.class);
        MethodHandle mh2 = lookup.findVirtual(String.class, "length", mt2);
        int len = (int) mh2.invoke("hello");    // 5
        
        // ③ findConstructor:new ArrayList(int)
        MethodType mt3 = MethodType.methodType(void.class, int.class);
        MethodHandle mh3 = lookup.findConstructor(ArrayList.class, mt3);
        ArrayList<?> list = (ArrayList<?>) mh3.invoke(16);
        
        // ④ findGetter:访问字段
        MethodHandle mh4 = lookup.findGetter(Demo.class, "name", String.class);
    }
    
    private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

Lookup 的访问权限:

lookup() 在哪个类调用,就拥有哪个类的访问权限
  → 能访问该类的 private 成员(无需 setAccessible)

privateLookupIn(Target.class, lookup)  → 借用 Target 的权限(需 Target 主动配合)
publicLookup()                          → 只能访问 public 成员
1
2
3
4
5

# 3.3 invoke vs invokeExact

这是 MethodHandle 最关键、也最容易踩坑的设计:

MethodHandle mh = lookup.findStatic(Math.class, "max",
    MethodType.methodType(int.class, int.class, int.class));

// 方式 1:invokeExact —— 类型必须 100% 严格匹配
int r1 = (int) mh.invokeExact(3, 5);          // ✅ OK
int r2 = (int) mh.invokeExact(3L, 5L);        // ❌ WrongMethodTypeException
Integer r3 = (Integer) mh.invokeExact(3, 5);  // ❌ 返回类型不严格匹配

// 方式 2:invoke —— 允许自动类型转换(asType)
int r4 = (int) mh.invoke(3L, 5L);             // ✅ OK,自动 long → int 截断
Integer r5 = (Integer) mh.invoke(3, 5);       // ✅ OK,自动 int → Integer 装箱
1
2
3
4
5
6
7
8
9
10
11

关键差异:

维度 invokeExact invoke
类型检查 编译期 + 运行期严格匹配 运行期允许自动转换
性能 接近直接调用 多一次 asType 转换
错误类型 WrongMethodTypeException 自动尝试转换

为什么严格?因为 invokeExact 在字节码层面是多态签名(polymorphic signature)——签名由调用点决定,编译器要把 mh.invokeExact(3, 5) 编译成"返回 int、参数 (int,int)"的 indy 调用,类型不匹配则无法生成正确的字节码。

铁律:性能敏感路径必须用 invokeExact。

# 3.4 句柄变换 asType/bindTo/insertArguments

MethodHandle 是可组合的一等公民——能像函数一样变换、绑定、组合:

MethodHandle add = lookup.findStatic(Math.class, "addExact",
    MethodType.methodType(int.class, int.class, int.class));

// ① asType:类型适配
MethodHandle longAdd = add.asType(MethodType.methodType(long.class, long.class, long.class));
long r1 = (long) longAdd.invokeExact(3L, 5L);    // 8

// ② bindTo:绑定第一个参数(柯里化)
MethodHandle add5 = add.bindTo(5);    // 注意:bindTo 仅适用于实例方法的 receiver
                                       // ★ 静态方法用 insertArguments 更合适

// ③ insertArguments:插入参数
MethodHandle addTo10 = MethodHandles.insertArguments(add, 0, 10);    // 把第 0 位固定为 10
int r2 = (int) addTo10.invokeExact(5);    // 15

// ④ filterArguments:参数预处理
MethodHandle parseInt = lookup.findStatic(Integer.class, "parseInt",
    MethodType.methodType(int.class, String.class));
MethodHandle addStrings = MethodHandles.filterArguments(add, 0, parseInt, parseInt);
int r3 = (int) addStrings.invokeExact("3", "5");    // 8

// ⑤ guardWithTest:条件分支
MethodHandle guarded = MethodHandles.guardWithTest(test, target, fallback);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

这套组合 API 让 MethodHandle 成为 JVM 上的"函数式编程基础设施"——比 Java 8 的 Function 更底层、更高效。

# 4. invokedynamic 与 LambdaMetafactory

# 4.1 CallSite 三种形态

CallSite 是 indy 调用点的"槽位"——内含一个 MethodHandle target:

public abstract class CallSite {
    MethodHandle target;
    public abstract MethodHandle dynamicInvoker();
    public abstract void setTarget(MethodHandle newTarget);    // 子类决定是否允许
}
1
2
3
4
5

三种实现:

子类 特征 用途
ConstantCallSite target 一旦设置就不可改 Lambda(绑定后稳定)
MutableCallSite target 可改,但更新非线程立即可见 JRuby 等动态语言的 inline cache
VolatileCallSite target 改变立即跨线程可见 罕见,用于热替换场景

# 4.2 引导方法 BootstrapMethod

每个 indy 指令都关联一个 BootstrapMethod(引导方法)——首次执行时调用,返回 CallSite:

// BootstrapMethod 签名约定(必须遵守)
public static CallSite bootstrap(
    MethodHandles.Lookup lookup,    // 调用方的 Lookup
    String name,                     // 调用点名称
    MethodType type,                 // 调用点签名
    Object... args                   // 静态参数(来自常量池)
) { ... }
1
2
3
4
5
6
7

字节码层证据(用 javap -v 反编译 Lambda):

BootstrapMethods:
  0: #N invokestatic java/lang/invoke/LambdaMetafactory.metafactory:
            (Ljava/lang/invoke/MethodHandles$Lookup;
             Ljava/lang/String;
             Ljava/lang/invoke/MethodType;
             Ljava/lang/invoke/MethodType;
             Ljava/lang/invoke/MethodHandle;
             Ljava/lang/invoke/MethodType;)
            Ljava/lang/invoke/CallSite;
    Method arguments:
      #M ()V                            // 函数式接口签名
      #X invokestatic Demo.lambda$0:()V  // 真正执行的目标
      #M ()V                            // 强制类型签名
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.3 LambdaMetafactory 落地链路

把 27 篇的 Lambda 与本篇的 MethodHandle 完整串起来:

flowchart TB
    A["源码<br/>Runnable r = () -> System.out.println(hello)"] --> B["javac 编译"]
    B --> C["字节码:indy 调用 LambdaMetafactory.metafactory<br/>+ Lambda 体编译为 lambda$0 私有方法"]
    C --> D["首次执行:indy 触发 BootstrapMethod"]
    D --> E["LambdaMetafactory.metafactory 返回 ConstantCallSite"]
    E --> F["CallSite.target 是一个 MethodHandle<br/>该 MethodHandle 创建 Runnable 实例"]
    F --> G["后续执行:直接 CallSite.dynamicInvoker.invoke<br/>性能接近直接 new 内部类"]
1
2
3
4
5
6
7

关键洞察:Lambda 不是创建 1 个对象——它创建的是"动态生成的 Runnable 子类的单例",LambdaMetafactory 通过 ASM 在运行时生成这个子类。详见 27 篇。

# 4.4 SwitchBootstraps 串讲

回扣 30 篇结尾的钩子——switch 模式底层:

String describe(Object obj) {
    return switch (obj) {
        case Integer i  -> "int: " + i;
        case String s   -> "string: " + s;
        case null       -> "null!";
        default         -> "other";
    };
}
1
2
3
4
5
6
7
8

字节码(简化):

aload_1
invokedynamic #N    // ★ 引导方法 SwitchBootstraps.typeSwitch
                    //   静态参数:[Integer.class, String.class]
                    //   返回值:匹配的 case 索引(0 / 1 / -1 表 default)
tableswitch
    0:  int_branch
    1:  string_branch
    -1: default_branch
1
2
3
4
5
6
7
8

SwitchBootstraps.typeSwitch 实现(位于 java.lang.runtime):

public static CallSite typeSwitch(
    MethodHandles.Lookup lookup,
    String invocationName,
    MethodType invocationType,
    Object... labels
) {
    // labels 数组:[Integer.class, String.class]
    // 返回 CallSite,target 是一个查表 MethodHandle
    return new ConstantCallSite(...);
}
1
2
3
4
5
6
7
8
9
10

至此,从语言层 switch → 字节码 indy → 运行时 SwitchBootstraps → MethodHandle 链路 → 实际类型检查的完整链条全部打通。

# 5. VarHandle 内存语义革命

# 5.1 为什么 Unsafe 必须退场

sun.misc.Unsafe 长期是高性能并发库的"基础设施"——AtomicInteger、ConcurrentHashMap、LongAdder、Disruptor 等无一例外依赖它。但 Unsafe 有 4 大原罪:

原罪 ①:包名 sun.* —— 不是公开 API,理论上 JVM 实现自由变更
原罪 ②:内存安全 —— 直接操作内存地址,越界即 JVM crash
原罪 ③:模块化破坏者 —— 绕过 JPMS 所有限制
原罪 ④:性能不一定最优 —— JIT 对 Unsafe 的优化次于 intrinsic
1
2
3
4

JDK 9 引入 VarHandle 作为官方继任者,提供与 Unsafe 等价的能力 + 更精细的内存语义控制 + JIT 友好。

# 5.2 四级访问模式

VarHandle 把内存访问按"语义强度"分为 4 级——这是 JDK 9 借鉴 Java Memory Model 与 C++11 atomic 的成果:

访问模式(由弱到强):

① Plain        —— 普通读写,无任何同步保证(等价普通字段访问)
② Opaque       —— 保证不被 JIT 重排到无关代码外,但不跨线程可见
③ Acquire/Release —— 单向屏障(acquire 防后续重排到前/release 防前面重排到后)
④ Volatile     —— 全屏障(等价 volatile 读写)
1
2
3
4
5
6

API 全景:

public class Counter {
    
    private static final VarHandle VALUE;
    
    static {
        try {
            VALUE = MethodHandles.lookup()
                .findVarHandle(Counter.class, "value", long.class);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    private long value;
    
    public void demo() {
        // ① Plain
        long v1 = (long) VALUE.get(this);
        VALUE.set(this, 1L);
        
        // ② Opaque
        long v2 = (long) VALUE.getOpaque(this);
        VALUE.setOpaque(this, 2L);
        
        // ③ Acquire/Release
        long v3 = (long) VALUE.getAcquire(this);
        VALUE.setRelease(this, 3L);
        
        // ④ Volatile
        long v4 = (long) VALUE.getVolatile(this);
        VALUE.setVolatile(this, 4L);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

何时选哪个:

场景 推荐 理由
普通字段 Plain 性能最高
flag 标志位(弱可见) Opaque 保证读到最终值,不一定立即可见
Disruptor 序号发布 Release/Acquire 单向屏障,性能优于 volatile
共享状态(强可见) Volatile 等价 volatile 字段

# 5.3 CAS 与 weakCompareAndSet

VarHandle 提供完整的 CAS 家族:

public class AtomicCounter {
    
    private static final VarHandle VALUE;
    static {
        try {
            VALUE = MethodHandles.lookup()
                .findVarHandle(AtomicCounter.class, "value", long.class);
        } catch (Exception e) { throw new ExceptionInInitializerError(e); }
    }
    
    private volatile long value;
    
    // ① compareAndSet:强 CAS,全屏障
    public boolean cas(long expected, long update) {
        return VALUE.compareAndSet(this, expected, update);
    }
    
    // ② weakCompareAndSet:可能虚假失败,但性能更高(在循环中很合适)
    public long incrementWeak() {
        long current;
        do {
            current = (long) VALUE.getVolatile(this);
        } while (!VALUE.weakCompareAndSet(this, current, current + 1));
        return current + 1;
    }
    
    // ③ compareAndExchange:返回当前值,失败时不重试
    public long casOrCurrent(long expected, long update) {
        return (long) VALUE.compareAndExchange(this, expected, update);
    }
    
    // ④ getAndAdd:原子加并返回旧值
    public long incrementAndGet() {
        return (long) VALUE.getAndAdd(this, 1L) + 1;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 5.4 内存屏障 fence API

VarHandle 还提供独立的内存屏障 API(在 MethodHandles 中):

// loadLoad + loadStore:防止后续读重排到前面
VarHandle.acquireFence();

// storeStore + loadStore:防止前面写重排到后面
VarHandle.releaseFence();

// 全屏障,等价 volatile 读写
VarHandle.fullFence();

// store 屏障:防止前面 store 重排到后面 store
VarHandle.storeStoreFence();

// load 屏障:罕见
VarHandle.loadLoadFence();
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Disruptor 风格的发布/订阅:

public class DisruptorRingBuffer {
    
    private long sequence = -1;     // Plain
    private final Object[] data;
    
    // 生产者发布
    public void publish(int slot, Object value) {
        data[slot] = value;
        VarHandle.releaseFence();    // ★ 保证 data 写入早于 sequence 更新可见
        sequence = slot;             // 此处用 SEQUENCE.setRelease 更佳
    }
    
    // 消费者读取
    public Object consume(int expected) {
        if (sequence < expected) return null;
        VarHandle.acquireFence();    // ★ 保证看到 sequence 之后再读 data
        return data[expected];
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6. 性能实证

# 6.1 JMH 三方对比

测试环境:JDK 21 + G1,调用 Math.max(int, int) 1 亿次:

Benchmark                          Mode   Cnt    Score  Error  Units
direct                             avgt    10    1.823 ± 0.042 ns/op   ← baseline
methodHandleInvokeExact            avgt    10    2.117 ± 0.067 ns/op   ← 1.16×
methodHandleInvoke                 avgt    10    8.452 ± 0.213 ns/op   ← 4.6×
reflectionCached                   avgt    10   12.631 ± 0.305 ns/op   ← 6.9×
reflectionUncached                 avgt    10  185.421 ± 4.281 ns/op   ← 102×
1
2
3
4
5
6

关键结论:

✅ MethodHandle.invokeExact ≈ 直接调用       (差距 < 20%)
⚠ MethodHandle.invoke 慢一档              (asType 转换开销)
⚠ 缓存的反射慢 7 倍                       (Inflation + setAccessible 检查)
❌ 未缓存反射慢 100 倍                    (查找 + 校验 + 调用)
1
2
3
4

# 6.2 invokeExact 才是性能之王

为什么 invokeExact 这么快?

JIT 编译期:
  ① invokeExact 是多态签名,签名由调用点决定
  ② JIT 知道目标 MethodHandle 的具体类型
  ③ JIT 把 invokeExact 内联成对应字节码(如 invokestatic)
  ④ 内联后被去优化保护(类型变化时去优化)

→ 稳定后字节码与直接调用几乎一致
1
2
3
4
5
6
7

对比反射:

Method.invoke 的开销链:
  ① 检查 setAccessible 状态
  ② 检查参数类型(数组装箱/拆箱)
  ③ 装箱整型参数 → Integer[]
  ④ 通过 NativeMethodAccessorImpl / GeneratedMethodAccessor 调用
  ⑤ 拆箱返回值

→ 即使 Inflation 优化(高频调用切到 GeneratedMethodAccessor),
  仍比 invokeExact 慢 5-7 倍
1
2
3
4
5
6
7
8
9

# 6.3 字段访问的性能阶梯

字段读写的性能(10 亿次 long 读取):

direct field access                  : 0.51 ns/op    1×
VarHandle.get (plain)                : 0.53 ns/op    1.04×
VarHandle.getOpaque                  : 0.55 ns/op    1.08×
VarHandle.getAcquire                 : 0.69 ns/op    1.35×
VarHandle.getVolatile                : 0.94 ns/op    1.84×
Unsafe.getLong (plain)               : 0.55 ns/op    1.08×
Unsafe.getLongVolatile               : 0.93 ns/op    1.83×
Field.get (cached, setAccessible)    : 5.31 ns/op    10.4×
1
2
3
4
5
6
7
8

结论:

  • ✅ VarHandle plain 与直接访问差距 < 5%
  • ✅ VarHandle 与 Unsafe 性能基本持平
  • ✅ 反射比 VarHandle 慢 10 倍以上

# 6.4 性能差异的根因

反射:解释执行 + 反射框架开销
  Method.invoke()
    ↓
  MethodAccessor.invoke()
    ↓
  NativeMethodAccessorImpl (前 N 次)
  GeneratedMethodAccessor   (后续,字节码生成)
    ↓
  反射不变量检查 + 装箱 + JNI 调用
  
MethodHandle:JIT 内联 + intrinsic
  invokeExact()
    ↓
  JIT 看到具体 MethodHandle 类型
    ↓
  内联为对应的 invokestatic / invokevirtual
    ↓
  与直接调用相同字节码

VarHandle:JIT intrinsic
  getVolatile()
    ↓
  JIT 直接生成 volatile 读字节码 + memory barrier
    ↓
  与 volatile 字段访问性能一致
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 7. 实战案例

# 7.1 高性能动态字段读取器

场景:序列化框架需要按字段名读取对象字段——传统反射太慢,用 MethodHandle 提速 5 倍:

public class FastFieldReader {
    
    // 字段名 → MethodHandle 缓存
    private final Map<String, MethodHandle> getters = new ConcurrentHashMap<>();
    private final Class<?> targetClass;
    
    public FastFieldReader(Class<?> clazz) throws Exception {
        this.targetClass = clazz;
        MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
        
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            // ★ 一次性构建 MethodHandle,后续直接 invokeExact
            MethodHandle mh = lookup.unreflectGetter(field)
                .asType(MethodType.methodType(Object.class, Object.class));    // 统一签名为 (Object)Object
            getters.put(field.getName(), mh);
        }
    }
    
    public Object read(Object instance, String fieldName) throws Throwable {
        MethodHandle mh = getters.get(fieldName);
        if (mh == null) throw new IllegalArgumentException("no such field: " + fieldName);
        return mh.invokeExact(instance);    // ★ invokeExact 性能接近直接访问
    }
}

// 性能对比(百万次读取):
// FastFieldReader.read    : 18 ms     ★
// Field.get (cached)      : 95 ms     ← 5× 慢
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 7.2 手撕 Switch 模式底层

复刻一个迷你版 SwitchBootstraps,理解 indy 引导机制:

public class MiniSwitch {
    
    /** 自定义 BootstrapMethod,签名严格遵守 indy 约定 */
    public static CallSite typeSwitch(
        MethodHandles.Lookup lookup,
        String name,
        MethodType type,
        Object... labels    // labels 是 Class<?>[]
    ) throws Throwable {
        
        // 实际目标:根据 obj 类型返回索引
        MethodHandle target = lookup.findStatic(
            MiniSwitch.class, "doMatch",
            MethodType.methodType(int.class, Object.class, Object[].class));
        
        // 把 labels 数组绑定为第二个参数
        target = MethodHandles.insertArguments(target, 1, (Object) labels);
        
        // 返回常量调用点
        return new ConstantCallSite(target.asType(type));
    }
    
    public static int doMatch(Object obj, Object[] labels) {
        if (obj == null) return -1;
        for (int i = 0; i < labels.length; i++) {
            if (((Class<?>) labels[i]).isInstance(obj)) return i;
        }
        return -1;
    }
}

// 该 BootstrapMethod 可被 indy 字节码引用
// JDK 21 的 SwitchBootstraps.typeSwitch 实现思路与此一致,
// 区别仅在于:使用 ClassValue 缓存 + 处理 enum / sealed 等更多 case 形态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 7.3 无锁原子计数器

使用 VarHandle 替代 Unsafe 实现高性能计数器:

public class FastCounter {
    
    private static final VarHandle VALUE;
    
    static {
        try {
            VALUE = MethodHandles.lookup()
                .findVarHandle(FastCounter.class, "value", long.class);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    @SuppressWarnings("unused")    // 通过 VarHandle 访问
    private volatile long value;
    
    /** 高竞争场景:weakCompareAndSet 减少 CAS 失败重试 */
    public long incrementAndGet() {
        long current;
        long next;
        do {
            current = (long) VALUE.getVolatile(this);
            next = current + 1;
        } while (!VALUE.weakCompareAndSet(this, current, next));
        return next;
    }
    
    /** 低竞争场景:直接 getAndAdd 性能更优 */
    public long fastIncrement() {
        return (long) VALUE.getAndAdd(this, 1L) + 1;
    }
    
    public long get() {
        return (long) VALUE.getVolatile(this);
    }
}

// 与 AtomicLong 性能对比(单线程,1 亿次自增):
// AtomicLong.incrementAndGet       : 1.92 ns/op
// FastCounter.fastIncrement        : 1.93 ns/op    ★ 等价(AtomicLong 内部就是 VarHandle)
// 
// JDK 9+ 后,AtomicInteger / AtomicLong 全部改用 VarHandle 实现,Unsafe 已淡出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 8. 使用边界与陷阱

# 8.1 invokeExact 类型严格匹配

最常见的错误:

MethodHandle mh = lookup.findStatic(Math.class, "max",
    MethodType.methodType(int.class, int.class, int.class));

// ❌ 错误 1:返回值未强转
Object r1 = mh.invokeExact(3, 5);    // WrongMethodTypeException
                                      // 调用点签名变成 (int,int)Object,与 mh 不符

// ❌ 错误 2:参数类型不匹配
int r2 = (int) mh.invokeExact(3L, 5);    // long 不能直接当 int

// ✅ 正确写法
int r3 = (int) mh.invokeExact(3, 5);
1
2
3
4
5
6
7
8
9
10
11
12

铁律:调用 invokeExact 时,强转返回值 + 参数类型 100% 严格匹配 MethodType。

# 8.2 Lookup 模块封装

JDK 9 模块化后,Lookup 不能跨模块"白嫖"私有访问:

// ❌ 在模块 A 里查找模块 B 的私有成员
MethodHandles.Lookup lookup = MethodHandles.lookup();    // A 模块的 lookup
lookup.findVirtual(B.class, "privateMethod", ...);
// → IllegalAccessException

// ✅ 正确做法:B 模块在 module-info 中 opens
//    module B { opens com.b.internal to A; }

// ✅ 或使用 privateLookupIn(要求目标模块允许)
MethodHandles.Lookup privLookup = MethodHandles.privateLookupIn(B.class, lookup);
1
2
3
4
5
6
7
8
9
10

# 8.3 VarHandle 必须 static final

性能关键:VarHandle 必须声明为 static final,否则 JIT 无法识别为 intrinsic:

public class Wrong {
    private VarHandle handle;    // ❌ 非 static final,性能退化到反射级别
    
    public Wrong() throws Exception {
        handle = MethodHandles.lookup().findVarHandle(...);
    }
}

public class Right {
    private static final VarHandle HANDLE;    // ✅ JIT 可特化
    
    static {
        try {
            HANDLE = MethodHandles.lookup().findVarHandle(...);
        } catch (Exception e) { throw new ExceptionInInitializerError(e); }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 8.4 反模式清单

反模式 问题 正确做法
用 invoke 替代 invokeExact 性能损失 4 倍以上 性能敏感路径用 invokeExact
MethodHandle 不缓存 每次 lookup 重建 用 static final 缓存
VarHandle 非 static final JIT 无法 intrinsic 必须 static final
用 setVolatile 替代 set 性能损失 2 倍 弱可见时用 plain/opaque
MethodHandle 当反射临时替代 没用上 invokeExact 优势 真热点才用,冷路径反射够了
跨模块直接 findVirtual IllegalAccessException privateLookupIn 或 opens
用 Unsafe 替代 VarHandle JDK 24+ 风险 全部迁移到 VarHandle
BootstrapMethod 写复杂逻辑 首次调用慢 引导方法只做一次性绑定

# 9. 综合案例串讲

# 9.1 双案例真相揭晓

① §1.1 反射热路径 35% CPU 真相:缓存的 Method.invoke 仍然慢 20 倍——根因不是查找开销,而是装箱 + 反射框架不变量检查 + JNI 调用的固有成本(§6.4)。即使 JVM Inflation 优化把高频调用切到 GeneratedMethodAccessor,方法调用链仍比直接调用多 4~5 层。正解:用 MethodHandle.invokeExact 替代——JIT 内联后字节码与直接调用一致,性能差距 < 20%(§6.1)。

② §1.2 Unsafe 退场恐慌真相:Unsafe 的 4 大原罪让它注定退场(§5.1)——JDK 24 已计划全面禁用 Unsafe 的 memory access API。官方继任者是 VarHandle——不仅功能等价,还提供更精细的 4 级内存语义控制(§5.2),且 JDK 9 起 AtomicInteger/Long、ConcurrentHashMap 等核心库已全部迁移到 VarHandle,性能与 Unsafe 持平(§6.3)。

③ 7 大追问全部作答:

追问 答案 章节
① MethodHandle 比反射快多少 invokeExact 接近直接调用,比反射快 5-7 倍 §6.1、§6.4
② MethodType 与 invokeExact 严格性 多态签名 + 编译期严格匹配 §3.1、§3.3
③ MethodHandle 与 indy 关系 indy 的调用目标就是 MethodHandle §4
④ VarHandle 替代 Unsafe 凭什么 公开 API + 4 级内存语义 + JIT intrinsic §5
⑤ 四级内存语义区别 plain/opaque/acquire-release/volatile 由弱到强 §5.2
⑥ JMH 性能实测 invokeExact 1.16× / 缓存反射 7× / 未缓存反射 100× §6.1
⑦ 实战落地 字段读取器/SwitchBootstraps/无锁计数器 §7

# 9.2 一次句柄调用的旅行

把"mh.invokeExact(target, arg)"从源码到 CPU 执行的完整生命线串起来:

T 0     源码:
        MethodHandle mh = lookup.findVirtual(String.class, "length",
            MethodType.methodType(int.class));
        int len = (int) mh.invokeExact("hello");

T+0     编译期:
        [§3.3] javac 看到 invokeExact,生成多态签名调用点
        [§4.2] indy 字节码? 不!invokeExact 是 invokevirtual,但带 polymorphic 标记
        [§3.1] MethodType.methodType(int.class) 在常量池中驻留

T+ 5ns  类加载:
        [§3.2] lookup.findVirtual 查找 String.length 方法
        [§3] 创建 MethodHandle 实例(DirectMethodHandle.SimpleVirtual)

T+1μs   首次 invokeExact 调用:
        [§3.3] 检查 caller 类型签名 == mh.type()
        [§3] 反射式查找 + 校验,生成调用 stub
        [§3.4] MethodHandle 链路:DirectMethodHandle → invokeBasic → 目标方法

T+10μs  JIT 介入(C1):
        [14篇] hot 调用点被 JIT 编译
        [§6.4] JIT 看到 mh 是 final 字段(JIT 可常量折叠)
        [§6.4] JIT 把 invokeExact 内联为直接的 invokevirtual length

T+1ms   JIT 完成(C2):
        [14篇] C2 进一步内联 length 方法本体
        [01篇] String 的 char[]/byte[] 直接读取
        最终机器码:1 条 movzx + 1 条 ret

T+50ms  稳定状态:
        [§6.1] 实测 2.1 ns/op,与直接调用 1.8 ns/op 差距 < 20%
        若类型变化(mh 字段被改)→ 去优化(§14篇)→ 重新编译

跨篇引用全景:
  [07篇] 反射 → MethodHandle 是反射的现代继任者
  [13篇] 字节码 → invokevirtual / invokedynamic
  [14篇] JIT → invokeExact 被特化内联
  [25/26篇] 注解处理 → MethodHandle 不依赖反射框架
  [27篇] Lambda → invokedynamic + LambdaMetafactory
  [30篇] Switch 模式 → invokedynamic + SwitchBootstraps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 9.3 设计哲学回扣

收官提炼三条工程哲学:

  1. "运行时句柄"是动态语言基础设施的灵魂:MethodHandle/VarHandle 表面上看是"反射的现代替代",但本质是 JVM 为支持动态语言(JRuby/Groovy/Kotlin/Scala)而打造的通用调用基础设施。Lambda、Stream、Switch 模式、字符串拼接、Record 自动 equals——所有这些 Java 8 之后的语言特性,底层全部基于 MethodHandle + invokedynamic。这告诉我们:好的语言演进不靠"特殊化"——它靠把基础设施做强,让新特性自然涌现。当你看到 30 篇的 switch (event),应当意识到那不是 30 个 if-else 的语法糖,而是 SwitchBootstraps + ConstantCallSite + MethodHandle 链路的精心编排。

  2. "性能 = 接近原生"才是真本事:反射慢是因为它多了一层框架——参数装箱、类型校验、JNI 跳转、Inflation 切换。而 MethodHandle/VarHandle 的设计哲学是——让 JIT 能把它们识别为 intrinsic,进而内联为对应的字节码指令。invokeExact 严格类型匹配看似刁难,实则是为 JIT 提供"我就是这个签名"的强保证;VarHandle 必须 static final 看似限制,实则是让 JIT 能做常量折叠。API 的"严格"换来的是"零开销抽象"——这是从 C++ 到 Rust 一脉相承的工程美学,Java 也终于站到了这个高度。

  3. "内存语义显式化"是并发编程的成熟标志:从 09 篇的 volatile/JMM 到本篇的 4 级访问模式——Java 并发设计的演进路径是**"先把规则写清楚,再让程序员选择强度"**。早年只有 plain 和 volatile 两档,开发者要么"什么都不保证"要么"全屏障 + 性能损失";如今 plain → opaque → acquire/release → volatile 让你按需付费——保证最弱、性能最高的那一档。这与 C++11 atomic 的 memory_order 系列设计哲学完全一致。当语言开始让你"显式选择内存语义",意味着这门语言的并发模型成熟了——你不再是"靠经验和迷信"写并发代码,而是"按规约和测量"写并发代码。这是从匠人到工程师的分水岭。

# 9.4 速查表

MethodHandle 速查:

// 1. 拿 Lookup
MethodHandles.Lookup lookup = MethodHandles.lookup();

// 2. 构建 MethodType
MethodType mt = MethodType.methodType(returnType, paramTypes...);

// 3. 五大查找
lookup.findStatic(C.class, "name", mt)       // 静态方法
lookup.findVirtual(C.class, "name", mt)      // 虚方法
lookup.findSpecial(C.class, "name", mt, c)   // super/private
lookup.findConstructor(C.class, mt)          // 构造器
lookup.findGetter(C.class, "field", T.class) // 字段读

// 4. 调用(性能敏感用 invokeExact)
int r = (int) mh.invokeExact(args...);

// 5. 句柄变换
mh.asType(newType)                              // 类型适配
mh.bindTo(receiver)                             // 绑定接收者
MethodHandles.insertArguments(mh, pos, vals)    // 插入参数
MethodHandles.filterArguments(mh, pos, filters) // 参数预处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

VarHandle 速查:

// 必须 static final
private static final VarHandle X = MethodHandles.lookup()
    .findVarHandle(C.class, "field", T.class);

// 4 级访问
X.get(this)          / X.set(this, v)            // plain
X.getOpaque(this)    / X.setOpaque(this, v)      // opaque
X.getAcquire(this)   / X.setRelease(this, v)     // acquire-release
X.getVolatile(this)  / X.setVolatile(this, v)    // volatile

// CAS 家族
X.compareAndSet(this, e, u)            // 强 CAS
X.weakCompareAndSet(this, e, u)        // 弱 CAS(循环中用)
X.compareAndExchange(this, e, u)       // 失败返回当前值
X.getAndAdd(this, delta)               // 原子加
X.getAndSet(this, v)                   // 原子换

// 屏障(罕用)
VarHandle.acquireFence();
VarHandle.releaseFence();
VarHandle.fullFence();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

性能选择口诀:

方法调用:
  ① 编译期已知 → 直接调用
  ② 运行期已知 + 性能敏感 → MethodHandle.invokeExact
  ③ 运行期已知 + 一次性 → 反射也行
  ④ 完全动态(语言互操作)→ invokedynamic + BootstrapMethod

字段访问:
  ① 编译期已知 → 直接 get/set
  ② 跨线程可见性 → VarHandle.getVolatile / setVolatile
  ③ Disruptor 风格高性能发布 → getAcquire / setRelease
  ④ 原子更新 → VarHandle.compareAndSet / getAndAdd
  ⑤ Unsafe 全部场景 → VarHandle 完全替代
1
2
3
4
5
6
7
8
9
10
11
12

下一篇进入 卷四第 32 篇:ASM/Javassist/ByteBuddy 字节码框架对比——承接本篇 LambdaMetafactory / SwitchBootstraps "运行时生成代码"的钩子,把"字节码增强"三大主流框架完整对比:从 ASM 的 visitor 模式 + 性能极限、Javassist 的源码层 API + 上手友好、到 ByteBuddy 的 DSL 链式 + Spring 6 默认选型——附手撕一个简易 Mock 框架的完整代码,帮助你理解 Mockito/JaCoCo/Skywalking 等中间件背后的"字节码黑魔法"。

上次更新: 2026/06/10, 11:13:41
反射机制与动态代理
三大字节码框架对比

← 反射机制与动态代理 三大字节码框架对比→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式