乐趣区

小师妹学JVM之JIT中的PrintAssembly续集

简介

上篇文章和小师妹一起介绍了 PrintAssembly 和 PrintAssembly 在命令行的使用,今天本文将会更进一步讲解如何在 JDK8 和 JDK14 中分别使用 PrintAssembly,并在实际的例子中对其进行进一步的深入理解。

JDK8 和 JDK14 中的 PrintAssembly

小师妹:F 师兄,上次你介绍的 PrintAssembly 的自测命令,怎么在 JDK14 中不好用呢?

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -version

有什么不好用的,命令不是正常打出来了吗?

小师妹:F 师兄,你看下我运行的结果,机器码下面展示的怎么是 448b 5608 这样的数字呀,不应该是 assembly language 吗?

嗯 … 小师妹的话让我陷入了深深的思考,究竟是什么导致了这样的反常的结果呢?是道德的沦丧还是人性的扭曲?

于是我翻遍了 baidu,哦,不对是 google,还是没有找到结果。

难点是 JDK14 有 bug?还是 JDK14 已经使用了另外的 Assembly 的实现?

有问题就解决问题,我们先从 JDK8 开始,来探索一下最原始的 PrintAssembly 的使用。

更多精彩内容且看:

  • 区块链从入门到放弃系列教程 - 涵盖密码学, 超级账本, 以太坊,Libra, 比特币等持续更新
  • Spring Boot 2.X 系列教程: 七天从无到有掌握 Spring Boot- 持续更新
  • Spring 5.X 系列教程: 满足你对 Spring5 的一切想象 - 持续更新
  • java 程序员从小工到专家成神之路(2020 版)- 持续更新中, 附详细文章教程

JDK8 中使用 Assembly

在 JDK8 中如果我们运行 Assembly 的测试命令,可以得到下面的结果:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -version

Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Could not load hsdis-amd64.dylib; library not loadable; PrintAssembly is disabled
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

这个故事告诉我们,虽然 PrintAssembly 开关打开了,但是系统不支持,缺少了 hsdis-amd64.dylib 文件。

这个 hsdis 是一个反汇编的工具,我们需要 hsdis 的支持才能在 JDK8 中使用 Assembly。

我是 mac 系统,下面是在 mac 系统怎么安装 hsdis:

hg clone http://hg.openjdk.java.net/jdk8u/jdk8u

cd jdk8u/hotspot/src/share/tools/hsdis/

wget http://ftp.heanet.ie/mirrors/ftp.gnu.org/gnu/binutils/binutils-2.30.tar.gz

tar -xzf binutils-2.30.tar.gz

make BINUTILS=binutils-2.30 ARCH=amd64

#java8
sudo cp build/macosx-amd64/hsdis-amd64.dylib /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/server/

#java9 onwards
sudo cp build/macosx-amd64/hsdis-amd64.dylib /Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents/Home/lib/server/

如果你是 linux 或者 windows 系统,请自行探索 hsdis 的安装方法。

按照步骤先把 java8 的 hsdis-amd64.dylib 安装好。

然后再次运行测试命令:

完美,汇编语言出现了。

JDK14 中的 Assembly

然后我想到,如果把这个 dylib 文件拷贝到 JDK14 相应的目录下面,运行一次会怎么样呢?

大家注意,JDK9 之后,使用了模块化,所以之前的目录结构发生了比较大的变化,大家参考上面我列出的地址。

再次运行测试代码:

大家看到,Assembly 又出现了,真的是让我热内盈亏。

其实最开始的时候,我发现 JDK14 中 Assembly 没能正常显示的时候,我也有想过拷贝一个 hsdis-amd64.dylib 过来试试,但是一看还需要下载 JDK 的代码,重新编译,就打起了退堂鼓。

吃一堑,长一智,下次遇到问题千万不能走捷径。抄近路害死人呀!

在 JMH 中使用 Assembly

Assembly 主要是为了进行代码调优或者理解 JVM 的运行原理来使用的。

这里我们举一个在 JMH 中使用 Assembly 的例子:

@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(value = 1,
        jvmArgsPrepend = {
        "-XX:+UnlockDiagnosticVMOptions",
                "-XX:CompileCommand=print,com.flydean.PrintAssemblyUsage::testPrintAssembly"
}
)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class PrintAssemblyUsage {

    int x;
    @Benchmark
    @CompilerControl(CompilerControl.Mode.DONT_INLINE)
    public void testPrintAssembly() {for (int c = 0; c < 1000; c++) {synchronized (this) {x += 0xFF;}
        }
    }
    public static void main(String[] args) throws RunnerException {Options opt = new OptionsBuilder()
                .include(PrintAssemblyUsage.class.getSimpleName())
                .build();

        new Runner(opt).run();}
}

上面的例子中,我们使用了 -XX:CompileCommand 指定要打印的方法,而不是输出所有的 Assembly,方便我们查看和分析结果。

总结

本文介绍了 JDK8 和 JDK14 中,怎么开启 PrintAssembly。并举了一个在 JMH 中使用的例子。

那么有人会问了,在 JMH 中使用 Assembly 到底有什么意义呢?别急,我们在后面深入 JVM 的本质中,马上就要讲到,敬请期待。

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/jvm-jit-printassembly-2/

本文来源:flydean 的博客

欢迎关注我的公众号: 程序那些事,更多精彩等着您!

退出移动版