本系列博文将以面试题为切入点,带你深入 Java 核心内容,提升 Java 内功修为。
面试官:谈谈你对 Java 平台的理解?“Java 是解释执行”,这句话正确吗?
1.对 Java 平台的理解
针对 Java 平台的理解,我们可以从多方面简明扼要的谈及一下,比如:Java 是一门面向对象的高级语言,具有 Compile once, run anywhere
(一次编译,到处运行) 的跨平台的特性;Java 语言的特性包括泛型, lambda 特性等;基础类库包括常用的 utils 包下面的集合,还有 IO/NIO, 网络,并发,安全等基础类库等。
在谈谈 JVM 的一些基础概念和机制,这其中包括 Java 的类加载机制,常用版本 JDK(如 JDK8) 内嵌的 Class-Loader, 例如 Bootstrap, Application 和 Extension Class-Loader(拓展类加载器);类的加载的大致过程:加载 -> 验证 -> 连接 -> 初始化;自定义 Class-Loader 等。还可以说说垃圾回收的基本原理,最常见的垃圾回收器,如 SerialGC、Parallel GC、 CMS、 G1 等。
还有 JDK 包含的一些工具,如编译器,运行时环境,安全工具,性能诊断(线上线程死锁,如何锁定问题?),监控工具等。
日常开发中,我们会经常接触到 JRE(java runtime environment) 或者是 JDK (java development kit)。JRE 也就是 java 运行时环境,它包含了 JVM 和 java 类库以及其他一些模块。
题外话:很多 java 开发者不是很清晰 JRE 和 JDK 之间的关系。
JDK, 其实就是 JRE 的一个超集,它提供了更多的工具,比如编译器,各种诊断工具等。
2.Java 究竟是不是解释执行呢?
说 Java 是解释执行这句话比较片面,也是不太准确的。我们开发出的以 .java
为后缀的源代码,会通过 javac 编译成 JVM 能够看懂的字节码,然后在运行时,通过 Java 虚拟机内嵌的解释器将字节码转换最终计算机能够看懂的机器码。
但是常见的 JVM , 比如我们生产环境经常使用的 Oracle JDK
提供的 Hotspot JVM
, 都提供了 JIT
(just in time) 编译器,也就是动态编译器,JIT
能够将程序中那些热点代码编译成机器码(大部分程序都是小部分的热点代码耗费了系统的大多数资源),这种情况下,所说的部分热点代码就属于编译执行的范畴,而不是解释执行。
备注:说到这里,也许还是有很多人很迷糊,到底解释执行和编译执行有啥区别?
举个形象的栗子,拿翻译官翻译的场景说事,解释执行就好比同声传译,而编译执行就好比提前翻译好,到时候直接放录音即可。
3.解释执行和编译执行知识面深入
我们都知道,Java 通常分为编译期和运行时。但是 Java 的编译和 C/C++ 又有不同的意义,我们说的编译通常是通过 javac 命令将 java 源代码编译成 .class 字节码文件,而不是能够直接运行的机器码。
在运行时,JVM 会通过类加载器(Class-Loader)加载字节码,解释执行或者编译执行。在主流的 JDK 版本(JDK 8)中, 实际上采用的是混合编译模式,即解释执行 + 编译执行。
另外,Java 虚拟机启动时,我们可以指定不同的参数对运行模式进行选择。比如,指定 -Xint
, 就是告诉 JVM 只进行解释执行,而不使用编译执行,这种模式抛弃了 JIT 带来的性能优势。对应的,我们还可以通过 -Xcomp
参数,告诉 JVM 关闭解释执行。我们可能会觉得这种模式,性能较高,那可未必。-Xcomp
会导致 JVM 启动变慢很多。
除了上面谈到的解释执行和编译执行,还有一种叫做 AOT(Ahead-of-Time Compilation), 直接将字节码编译成机器码,这样避免了 JIT 预热等方面的开销,JDK 9 中就映入了这种 AOT 特性,并且新增了 jaotc 工具。通过下面的命令把某个类或者某个模块编译成 AOT 库。
jaotc --output libHelloWorld.so HelloWorld.class
jaotc --output libjava.base.so --module java.base
然后,你就可以在启动的时候指定了:
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld