目录
简介
什么是 Intrinsic Methods
内置方法的特点
多样性
兼容性
java 语义的扩展
Hotspot VM 中的内置方法
intrinsic 方法和内联方法
intrinsic 方法的实现
Graal
总结
简介
内置方法是什么呢? 它和 inline method 有什么关系呢? 内置方法是怎么实现的呢? 所有的问题都可以在本文找到答案.
什么是 Intrinsic Methods
什么是内置方法呢?
维基百科给出的定义是这样的:
在计算机软件中, 按照编译器理论, 固有方法 (或内置方法) 是可在给定编程语言中使用的方法, 该编程语言的实现由编译器专门处理. 通常, 它可以将自动生成的指令序列替换为原始方法调用, 类似于内联方法. 与内联方法不同, 编译器对内置方法有深入的了解, 因此可以针对给定情况更好地集成和优化它.
实现内置方法的编译器通常仅在程序请求优化时才启用它们, 否则会退回到语言运行时环境提供的默认实现.
所以总结一下, 内置方法就是编译器内置的方法实现.
内置方法的特点
内置方法有什么特点呢? 我们在这里总结一下.
多样性
因为内置方法是在编译器内部实现的, 所以不同的虚拟机, 其内置方法是不一样的.
我们不能直接说哪个方法是内置方法, 因为不同的 JVM 是不同的.
兼容性
内置方法是在需要的时候才会使用的, 如果在不需要的时候则会回退到普通的方法实现, 也就是 java 代码的实现.
所以在 java 源代码级别来看, 内置方法和非内置方法是一样的. 他们的区别在于 JVM 的实现.
java 语义的扩展
有些方法用普通的 java 代码是无法实现的. 比如 sun.misc.Unsafe.compareAndSwapInt().
我们只能使用 JNI 或者内置方法来对其实现. 所以内置方法可以实现对 java 语义的扩展.
一般来说, JDK 和核心库中, 能使用内置方法优化都已经优化了. 所以我们在平时的代码调用中, 一定要尽可能的使用 JDK 的公共 API 和核心库, 这样才能充分利用内置方法的特性, 从而提升程序效率.
Hotspot VM 中的内置方法
那么对于 Hotspot VM 来说, 内置的方法有哪些呢?
Hotspot VM 中所有的内置方法都在 src/share/vm/classfile/vmSymbols.hpp 类中:
上图我只截取了部分标记为 intrinsic 方法的类的说明.
可以看到 java.lang.Math 中大部分的方法都是 intrinsic 的方法.
怎么查看我们代码中调用的方法是不是 intrinsic 方法呢?
很简单, 在 java 命令之前加上这些参数即可:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
举个最常用的查看 java 版本的例子:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining version
看下输出结果:
从结果可以很清楚的看到, java.lang.System.arraycopy 方法是内置方法.
另外我们可以通过更加底层的汇编语言来查看, 再添加
-XX:+PrintAssembly
我们看下输出结果:
invokestatic 意味着该方法就是 intrinsified 方法.
intrinsic 方法和内联方法
内联方法就是把调用方函数代码 "复制" 到调用方函数中, 减少因函数调用开销的技术.
intrinsic 方法大部分都是内联方法.
intrinsic 方法的实现
前面我们提到了内置方法是在编译器实现的.
在 Hotspot VM 中其实有 3 中编译器.
第一种就是 javac 将 java 源代码编译成为字节码.
在这一层, 只有一些 math 方法和 bootstrapping 的 MethodHandle 是在这一层实现的.
第二种就是在 JIT 的 Client Compiler (C1).
第三种就是在 JIT 的 Server Compiler (C2).
举一个例子, 我们看一下 java.lang.System.currentTimeMillis()方法:
- @HotSpotIntrinsicCandidate
- public static native long currentTimeMillis();
JDK 源码使用了 HotSpotIntrinsicCandidate 注解. 这个注解只是表示该方法可能会被用于 Intrinsic, 而并不意味着一定使用 Intrinsic.
这个方法在 Interpreter 级别是没有 intrinsified. 因为这是一个 native 方法, 所以会通过 JNI 调用底层的 C++ 实现.
而在 C1 和 C2 级别, 会使用 intrinsified, 直接调用 os::javaTimeMillis().
好处就是减少了 JNI 的使用, 提升效率.
好了问题来了, 我们可以自己实现 intrinsified 方法吗?
答案是可以, 不过需要修改底层的 JVM 实现.
这里有两个具体的例子, 感兴趣的大家可以自行研究.
C1 级别修改(First cut: C1 Class.isInstance intrinsic):
https://gist.github.com/rednaxelafx/2830194
C2 级别修改(Example (XS) of adding an intrinsic method to HotSpot C2. Patch against HS20-b12):
- https://gist.github.com/rednaxelafx/1986224
- Graal
因为 Hotspot VM 是用 C++ 编写的, 如果要添加 Intrinsic 方法, 对于那些不熟悉 C++ 的朋友来说就太难了.
没关系, Oracle 开发了一个项目叫做 Graal. Graal 是一个用 java 编写的新款 JIT 编译器.
Graal 是基于 Java 的 JIT 编译器, 是 JDK 9 中引入的实验性 Ahead-of-Time(AOT)编译器的基础.
开启 Graal 的参数:
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
通过 Graal, 我们可以用 java 来实现 Intrinsic 方法, 想想就让人兴奋.
总结
来源: https://www.cnblogs.com/flydean/p/jvm-intrinsic-method.html