从你接触java开发到现在,你对Java平台到底了解到什么程度?

问题:谈谈你对Java平台的理解?“Java是解释执行”,这句话正确吗?

典型回答

Java本身是一种面向对象的语言,最显著的特性又两个方面:

  • 一是所谓的“书写一次,到处运行”,跨平台能力强
  • 二是垃圾收集(GC,Garbage Collection),Java通过垃圾收集器回收分配内存,大部分情况下,程序员不需要自己操心内存的分配和回收。

我们日常会接触到JRE(Java Runtime Environment)或者JDK(Java Development Kit)。JRE也就是Java运行环境,包含了JVM和java类库,以及一些模块等。而JDK可以看作是JRE的一个超集,提供了更多工具,比如编译器、各种诊断工具等。

对于“Java是解释执行”这句话,这个说法不太准确。我们开发的Java的源代码,首先通过Javac编译成为字节码(bytecode),然后,在运行时,通过Java虚拟机(JVM)内嵌的解释器将字节码转换成为最终的机器码。

但是常见的JVM,比如我们大多数情况使用的Oracle JDK提供的 Hotspot JVM,都提供了JIT(Hust-In-Time)编译器,也就是通常所说的动态编译器,JIT能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行,而不是解释执行了。

考点分析

其实这个问题,问得有点笼统。题目本身是非常开放的,往往考察的是多个方面,比如,基础知识理解是否很清楚;是否掌握Java平台主要模块和运行原理等。很多面试者会在这种问题上吃亏,稍微紧张了一下,不知从何说起,就给出个很简略的回答。
对于这类笼统的问题,你需要尽量表现出自己的思维深入并系统化,Java知识理解得也比较全面,一定要避免让面试官觉得你是个“知其然不知其所以然”得人。
毕竟明白基本组成和机制,是日常工作中进行问题诊断或者性能调优等很多事情得基础,相信没有招聘方会不喜欢“热爱学习和思考”得面试者。
即使感觉自己得回答不是非常完善,也不用担心。我个人觉得这种笼统的问题,有时候回答得稍微片面也很正常,大多数有经验得面试官,不会因为一道题就对面试者轻易地下结论。通常会尽量引导面试者,把他得真实水平展现出来,这种问题就是做个开场热身,面试官经常会根据你的回答扩展相关问题。

知识扩展

回归整体,对于Java平台得理解,可以从很多方面简明扼要地谈一下,例如:

  • Java语言特性,包括泛型、Lambda等语言特性
  • 基础类库,包括集合、IO/NIO、网络、并发、安全等基础类库。对于我们日常工作应用较多地类库

对于我们日常工作应用较多地类库,面试前可以系统化总结一下,有助于临场发挥。
或者谈谈JVM地一些基础概念和机制,比如Java的类加载机制,常用版本JDK(如JDK 8)内嵌的Class-Loader,例如Bootstarp、Application和Extension Class-loader;类加在大致过程:

加载、验证、链接、初始化(参考《深入理解Java虚拟机》)、自定 Class-Loader等。

还有垃圾收集的基本原理,最常见的垃圾收集器,如
SerialGC、Parallel GC、 CMS、G1

对于适用于什么样的工作负载最好心里也有数。这些都是可以扩展的领域,我会在后面的专栏对此进行系统的介绍。
当然还有JDK包含哪些工具或者Java领域内其他工具等,如
编译器、运行时环境、安全工具、诊断和监控工具

这些基本工具是日常工作效率的保证,对于我们工作在其他语言平台上,同样有所帮助,很多都是触类旁通的。


下图是我总结的一个相对宽泛的蓝图供你参考:

不再扩展了,回到前面问到的解释执行和编译执行的问题。有些面试官喜欢在特定问题上“刨根问底儿”,因为这是进一步了解面试者对知识掌握程度的有效方法,我稍微深入探讨一下。
众所周知,我们通常把Java分为编译期运行时。这里说的Java的编译和C/C++是有着不同的意义的,Javac的编译,编译Java源码生成“.class”文件里面实际是字节码,而不是可以直接执行的机器码。

Java通过字节码和Java虚拟机(JVM)这种跨平台的抽象,屏蔽了操作系统和硬件的细节,这也是实现“一次编译,到处执行”的基础。

在运行时,JVM会通过类加载器(Class-Loader)加载字节码,解释或者编译执行。就像我前面提到的,主流Java版本中,如JDK8实际是解释和编译混合的一种模式,即所谓的混合模式(-Xmixed)。

通常运行在server模式的JVM,会进行上万次调用以收集足够的信息进行高效的编译,client模式这个门限是1500次。Oracle Hotspot JVM内置了两个不同的JIT compiler,C1对应前面说的client模式,适用于对启动速度敏感得到应用,比如普通Java桌面应用;C2对应server模式,它的优化是为长时间运行的服务器应用设计的。默认是采用所谓的分层编译(TieredCompilation)。这里不再扩展更多JIT的细节,没有必要一下子就钻进去,我会在后面介绍分层编译的内容。

Java虚拟机启动时,可以指定不同的参数对运行模式进行选择。比如,指定“-Xint”,就是告诉JVM只进行解释执行,不对代码进行编译,这种模式抛弃了JIT可能带来的性能优势。毕竟解释器(interpreter)是逐条读入,逐条解释运行的。与其相对应的,还有一个“-Xcomp”参数,这是告诉JVM关闭解释器,不要进行解释执行,或者叫作最大优化级别。那你可能会问这种模式是不是最高效啊?简单说,还真未必。“-Xcomp”会导致JVM启动变慢非常多,同时有些JIT编译器优化方式,比如分支预测,如果不进行profiling,往往并不能进行有效优化。

除了我们日常常见的Java使用模式,其实还有一种新的编译方式,即所谓的AOT(Ahead-of-Time Compilation),直接将字节码编译成机器代码,这样就避免了JIT预热等各方面的开销,比如Oracle 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

而且,Oracle JDK支持分层编译和AOT协作使用,这两者并不是二选一的关系。如果你有兴趣,可以参考相关文档。AOT也不仅仅是只有这一种方式,业界早就有第三方工具(如GCJ、Excelsior JETT)提供相关功能。

另外,JVM作为一个强大的平台,不仅仅只有Java语言可以运行在JVM上,本质上合规的字节码都可以运行,Java语言自身也为此提供了便利,我们可以看到类似Clojure、Scala、Groovy、JRuby、Jython等大量JVM语言,活跃在不同场景。

今天,我简单介绍了一下Java平台相关的一些内容,目的是提纲挈领地构建一个整体地印象,包括Java语言特性、核心类库与常用第三方类库、Java虚拟机基本原理和相关工具,希望对你有所帮助。

最后修改:2021 年 02 月 17 日