JVM内部函数简介

2025/04/02

1. 概述

在本文中,我们将了解什么是内部函数以及它们在Java和其他基于JVM的语言中如何工作。

2. 什么是内部函数?

内部函数是编译器或解释器对我们的编程语言进行特殊处理的函数,更具体地说,这是一种特殊情况,编译器或解释器可以出于各种原因用替代实现替换该函数。

编程语言通常会通过理解特定方法调用的特殊性来处理这个问题,并且每当我们调用这个方法时,结果行为都会有所不同。这使我们的代码看起来与正常代码没有什么不同,但编程语言的实现可以在特殊情况下进行干预以提供额外的好处。

它的确切工作方式因编程语言以及操作系统和硬件而异,但是,因为这些都是为我们处理的,所以我们通常不需要知道任何这些细节。

内部函数可以带来各种好处,用本机代码替换特定算法可以使它们性能更好,甚至可以利用操作系统的特定功能或底层硬件

3. JVM上的内部函数

JVM通过用替代版本替换对特定类的确切方法调用来实现内部函数。JVM自行处理此问题,所以它只适用于核心类和特定架构。它还允许仅交换某些方法,而不是交换整个类。

具体的工作方式因JVM而异,这不仅包括不同版本的JVM(例如Java 8与Java 11),这还包括不同的JVM目标(例如Linux与Windows),尤其是JVM供应商(Oracle与IBM)。在某些情况下,传递给JVM的某些命令行标志会影响它们。

这种多样性意味着无法仅根据应用程序确定哪些方法将被替换为内部方法,哪些方法不会被替换。根据运行应用程序的JVM,情况会有所不同。但这在某些情况下可能会导致令人惊讶的结果-包括仅通过更改所使用的JVM就可以获得显著的性能优势

4. 性能优势

内部函数通常用于实现相同代码的更高效版本,例如,通过利用正在运行的操作系统或CPU的实现细节。有时这是因为它可以使用更高效的实现,有时它甚至可以使用特定于硬件的功能。

例如,HotSpot JDK对java.lang.Math中的许多方法都有一个内部实现。根据确切的JVM,这些可能会使用CPU指令来执行所需的精确计算。

一个简单的测试就可以证明这一点。例如,以java.lang.Math.sqrt()为例。我们可以写一个测试:

for (int a = 0; a < 100000; ++a) {
    double result = Math.sqrt(a);
}

该测试执行100000次平方根运算,耗时约123毫秒。但是,如果我们将此代码替换为Math.sqrt()实现的副本:

double result = StrictMath.sqrt(a);

此代码执行相同的操作,但执行时间为166毫秒。通过复制实现而不是让JVM用内部版本替换它,时间增加了35%。

5. 不可能的实现

在其他情况下,内部函数用于无法在Java中实现代码的情况,这些通常用于非常低级的情况。

例如,让我们看一下java.lang.Thread类中的onSpinWait()方法。此方法指示此线程当前未执行任何工作,可以将CPU时间分配给另一个线程。要实现这一点,它需要在尽可能低的级别上工作。

x86架构的HotSpot JDK使用PAUSE操作码直接在CPU上实现此操作,实现此目的的唯一其他方法是使用对本机代码的JNI调用,而这样做所涉及的开销首先会抵消调用的好处。

6. 识别Java中的内部函数

不幸的是,没有可靠的方法来识别可能被内部版本替换的方法,这是因为不同的JVM甚至不同平台上的同一个JVM会针对不同的方法执行此操作。

但是,从Java 9开始使用Hotspot JVM时,所有可能被替换的方法都会使用@HotSpotIntrinsicCandidate标注。添加此注解不会自动导致方法被替换,实际上,这发生在底层JVM中。相反,JVM开发人员知道这些方法很特殊并且要小心使用它们。

如果其他JVM被识别,它们可能会以不同的方式处理此问题,这包括Java 8或更早版本中的Hotspot JVM。

7. 总结

我们不能编写依赖于内部函数的程序,因为没有办法知道它们在运行时JVM上是否可用。但是,它们是JVM可以用来改进程序工作方式的一种引人注目的方法。

这些内部函数可以(而且经常)被添加到JVM的新版本中。然后,这允许通过升级我们正在运行的JVM来改进我们已经运行的代码,因此这是确保我们与依赖和运行时保持同步的另一个原因。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章