前言
说到Java,相信大家都对这么一句口号很熟悉:
Write Once Run Everywhere
这也是SUN公司的为宣传Java跨平台特性的口号。但是,回到Java设计之初,他们要怎么实现这个口号呢?下面我们就来简单聊聊。
1. 跨平台
什么是跨平台,跨越的是什么平台?这里引用百度百科的词条
1.1 操作系统与硬件环境
单独说操作系统,很多人估计都没有联系到,对于操作系统而言,直接操作的是硬件:CPU、寄存器等。因此,操作系统与硬件是关系密切的。但又没有那么密切,怎么说?只要你能知道你面对的硬件是什么,用他们听得懂的语言(指令集)来沟通,从而进行兼容,也能运行。而实际上,由于市场竞争的需要,CPU厂商很多时候对不同指令集进行兼容。这也是我们能够在硬件上任意选择我们想要的操作系统进行安装的原因。
因此,我们跨平台的重点,就变成了跨操作系统。这意味不管在Windows还是Linux,还是其他的什么操作系统,都能运行。可能简单这样说,大家可能理解不够深刻。问题:Windows跟Linux的差异在哪里?为什么一个Windows的软件不能直接在Linux上运行?
1.1.1 可执行文件
Windows:我们所熟知的就是exe文件格式了。
Linux:elf,但一搬都不带文件格式后缀。
Mac:mach-o。
如果不出意外的话,我们的一个应用程序要想在不同的操作系统上运行,就要打包成不同的可执行文件。
2. 如何实现跨平台
既然不同操作系统的可执行文件不一样,那么有没有可能通过一个中间层来屏蔽这些差异呢?Java的答案就是Java虚拟机。
2.1 Class文件格式
熟悉Java的同学都知道,Java代码编译后,会生成class文件。
Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有任何分割符。
– 摘自《深入理解Java虚拟机》
有经验的同学知道通过class文件,可以反汇编看到对应的汇编代码。而这些代码中的指令有一整套Java汇编指令集,一一对应。
而正是由于Class文件格式,也使得Java虚拟机的语言无关性成为可能。不管是什么语言,只要编译成Class文件格式,就能在JVM中运行。
但是回到指令运行,再深入想一想,CPU不也是有指令集吗?Java汇编指令集跟他们有什么联系吗?不,没有联系。那么问题来了,既然没有联系,CPU不认识它,要怎么执行呢?由Java虚拟机进行翻译!
2.2 Java的编译
说到这个,相信很多人第一时间想到的就是把Java代码编译成class文件格式。但实际上,这个并不是真正的可执行代码。因此它只是一种中间代码。到了JVM里面,在运行时有两种方式将其转换为真正的机器码:解释执行/编译执行。
- 解释执行:当需要执行代码时,再将字节码翻译成机器码。由解释器负责。
- 编译执行:先将程序编译成机器码后,再运行。由JIT编译器负责。
最开始,Java1.0只有解释运行。但是这样一行一行地翻译,性能实在拉跨,这才有了后面的JIT编译。这里不展开,感兴趣的同学可以去了解一下,JIT怎么实现即时编译的。
3 Java虚拟机
到这里,相信你们都知道:从代码层面看,Java虚拟机需要完成:字节码->机器码。但是Java虚拟机就这一个功能吗?为什么它叫虚拟机?我们的虚拟机都是些什么东西?事情没有那么简单!
Java内存模型
PS:网上借来的网图,若侵权,请留言我删除。
但话说回来,Java虚拟机跟虚拟化技术可能没多大关系。说到底,它只是为Java应用提供虚拟的运行环境,将字节码翻译成机器码。说到底,它也只是操作系统中运行的众多进程中的一个。
虽然编译成二级制字节码的软件借助JVM实现了跨平台,但是JVM本身不是跨平台的,这也是我们通常在下载jdk/jre时需要选择操作系统的原因。
总结
- Java的跨平台能力来自于Java虚拟机。但Java虚拟机并不是Java语言的“专利”。不管什么语言,只要能产生class字节码文件,就能被JVM所识别和运行。
- Java虚拟机通过翻译/即时编译技术,将字节码转换为机器码,从而实现跨平台。
- Java通过引入中间层来屏蔽平台差异。而这其实可以作为系统设计的常用手段。例如消息中间件,利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。
后记
每次写这种稍有些深度的文章,总是要花费大量时间。还是没有融会贯通啊,继续努力吧。以上内容可能限于水平导致错漏,欢迎大家一起交流/批评指正。