从Java诞生至今已有二十余年,基于虚拟机的技术屏蔽了底层环境的差异,“一次编译,随处运行”的思想促进了整个IT上层技术应用产生了翻天覆地的变化。Java作为服务端应用语言的首选,确实大大降低了学习和应用的门槛。现实生活中,绝大多数Java程序员对于虚拟机的原理和实现了解并不深入。而随着互联网的极速发展,现在的Java服务端应用需要应对极高的并发访问和大量的数据交互,从机制和设计原理上了解虚拟机的核心原理和实现细节显然能够帮助Java程序员编写出更高效优质的代码。
为了不影响大家阅读体验,这里只写书中的一部分内容,如果想获取整本电子书的可以+wx:bjmashibing003 获取相关电子书
Java数据结构与面向对象
从Java算法到数据结构
如果非要用-句简单的话来概括何谓“编程" ,那么下面的这句话虽然不一-定能够反映“编程"的全貌,但至少能够从- -个侧面描述编程的本质:“编程就是使用合适的算法处理特定的数据结构。”同理,也可以使用下面这句话来概括什么是“程序":“程序就是算法与数据结构的有机结合。"
注:这里所说的算法是一种广义的概念,凡是能够完成有特定逻辑的一组指令的集合都可以称为算法。使用C#完成一个绚丽的PC版视窗是一种算法,使用C++完成一个基于epoll的I0框架是一种算法,使用VB为Excel写一个宏脚本是一种算法,使用Java完成了一个网页数据加载也是一种算法。这里的算法不局限于那些特定的用于解决數学问题的技术逻辑。
算法是指令驱动的,一条条指令按照- -定 顺序执行,彼此协作,最终实现某种特定的逻辑,便完成了特定的算法。Java 程序的算法由Java字节码指令所驱动。而数据结构往往会作为算法的输人、输出和中间产出,即使输出的是一个简单的整型数字,也是一-种数据结构。
本来设计算法是一一件挺快乐的事儿,当使用JavaScript完成了一个能够绘制柱状统计图的小组件的时候,内心- -定是激动无比的;或者当使用汇编完成了一一个哈希表设计的时候,- -定会觉得自个儿特厉害。但是当算法遇上了不同的操作系统,或者不同的硬件平台,一件原本很快乐的事情便硬生生地被变成一件痛苦的事儿。 例如,你想开发一款高并发 、高性能的网络通信组件,或者-种分布式的调度器,如果选择的不是Java或者Python.而是C. C++等语言,那么多线程的处理、网络接口的调用等与平台和硬件相关的算法- -定 会让你如火的热情降到冰点以下。詹爷之所以伟大就在于,他借助于JVM虚拟机和中间语言字节码,完成了指令的跨平台兼容,从而使得Java 算法能够兼容大部分主流平台。这直接导致算法又变成了一项有趣的活儿,开发者终于又可以集中精力于编写算法这一件事上面,再也不用担心自己的算法是否兼容其他平台,也不用关注底层不同的实现。如同耕耘-样,没有耕坏的地,只有累死的牛,换而言之,没有兼容不了的平台,只有写不出的程序。当年詹爷实现Java“write once, run anywhere"的终极武器是"Java 字节码"这种中间语言(ML)技术。这条技术路线的选择有其偶然性,但是更多的却是-一种必然性。虽然可以选择的技术实现方式有很多种,但是总体思路是被限制死的。
总而言之,在目前计算机硬件执行架构的限制下,可选的“技术路径”只有那么几条,总结起来只有以下3条:
1.直接编写目标硬件平台的机器码指令
这种方式最直接,也最有效,目标硬件平台所支持的指令该是什么就是什么,没有任何二义性,不会产生任何混淆,清清楚楚,明明白白。不过一般只有最底层的软件程序才需要使用这种方式,同时早期的软件程序也采用这种方式,毕竞那时候的编程语言还没有如今这么智能化。
2.使用高级语言编程,通过编译器实现兼容
可以这么说,如果在马路上拿起一块小石头随便往大街上扔过去,砸中的十有八九就是这种机制的高级编程语言,例如C、CH+、Delphi 等都是如此。这种机制要求比较高,不仅要求在不同的目标平台上安装对应的编译器,还需要软件程序本身针对不同的目标平台调用不同的底层API。从源代码,到编译器,到打包,再到编译后的目标文件乃至可执行程序,全部都是目标平台相关的,没有一个是可以只“write once" 就能实现“run anywhere" 的,需要多个不同平台版本的编评器,源程序也是如此,因此开发者比较痛苦。
3.通过虚拟机实现兼容性
这种方式就是以Java为代表的技术路线。使用这种机制实现兼容性的成本相当低,开发者只需要开发次源代码,只需要在某一种硬件平台上编译通过,然后便可以在任何目标平台上运行(说任何平台确实夸张了,事实上JVM也并没有通吃全部的平台,但是基本实现了几大主流硬件平台和操作系统的兼容性),开发者所需要做的仅仅是在不同的硬件平台上部署不同的虚拟机而已。这种成本相比诸如C、C++之类的高级编程语言动不动就要针对不同目标平台而修改底层API,已经几乎可以忽略不计,如果非要为它定一个市场价的话,那定是跳楼价了。
当然,除了这了种主要的方式,还有其他各种与此类似的实现方案,例如:Python作为一门脚本语言,不需要编译便可以直接运行,但是其在本质上还是与JVM虚拟机类似,因为Python的编译过程由解释器代劳,解释器将python鳊译为中间语言(类似于Java的字节码)并基于中间语言实现跨平台,在这一点上,与JVM完全保持一致。
比较这3种方式,可以发现,实现兼容性的思路基本是从原始笨拙向着高度智能的方向发展,最终达到不需要为了兼容性而额外写多份源代码的目标,即“write once"。
常量池解析
JVM要完成Java逻辑的执行,必须能够“读懂”Java 字节码文件。而Java字节码文件从总体上而言,其实主要包含三部分:常量池、字段信息和方法信息。其中常量池存储了字段和方法的相关符号信息,因此对常量池的解读便成为解读Java字节码文件的基础。本章主要分析JVM解析Java字节码文件中常量池的逻辑。本章内容难度属于中等,只要你稍微具备一点C/C++基础便能读懂,当然,即使完全不具备C/C++基础,读懂本章也不是难事,大家都是写程序的,而写程序的小伙伴们,智商都是蛮高的^ ^。常量池是Java字节码文件的核心,因此也是JVM解析字节码的重头戏。
要注意的是,这里所说的常量池并不等同于JVM内存模型中的常量区,这里的常量池仅仅是文件中的一堆字节码而已。但是字节码文件中的常量池与JVM内存区的常量区之间却有着千丝万缕的联系,因为JVM最终会将字节码文件中的常量池信息进行解析,并存储到JVM内存模型中的常量区。字节码文件中的常量池是Java编译器对Java源代码进行语法解析后的产物,只是这种初步解析产生的结果比较粗糙。里面包含了各种引用,信息不够直观。而JVM根据字节码文件中的常量池信息再进行二次解析,这种解析目标清晰,直奔终点,会还原出所有常量元素的全部信息,让内存与你所编写的Java源代码保持一致。在字节码文件中,用于描述常量池结构的字节码流所在的块区紧跟在魔数和版本号之后,因此JVM在解析完魔数与版本号后,接着便开始解析常量池。
为了不影响大家阅读体验,这里只写书中的一部分内容,如果想获取整本电子书的可以+wx:bjmashibing003 获取相关电子书
网友评论