Java编程详解(微课版)
上QQ阅读APP看书,第一时间看更新

1.5 Java虚拟机

1.5.1 Java虚拟机的起源与构造

“Java”这个词代表了4个相互关联的概念,分别为Java语言、Java API、Java Class文件格式、Java虚拟机。整个Java体系是基于Java 虚拟机构造的,正因为如此,才能实现Java的安全性和网络移动性。Java并非第一个采用“虚拟机”概念的体系,但却是第一个得到广泛运用的虚拟机平台。“虚拟”是一种隔离物理资源与逻辑资源的手段,Java虚拟机的“虚拟”则是用来隔离物理机器、底层操作系统与Java语言规范实现的手段。

Java虚拟机讲解

虽然Java是一种面向对象的语言,Java开发人员平时大量使用的是对象间的多态、组合(Composition)、委派(Delegation),但当开发人员讨论虚拟机的时候,看见的基本概念却是“栈(Stack)”和“堆(Heap)”。Java虚拟机中没有寄存器的概念,方法调用是采用“栈”进行的,这是一种安全、简捷的方法。

Java虚拟机通过类装载器支持对类的隔离,这也是Java实现安全性的基础。每个类都具有自己的命名空间,在具有不同安全级别的沙箱中运行,因此不会产生低安全级别的代码越权访问高级别代码的情况。类装载器的出现是Java虚拟机与大部分用C语言实现的虚拟机的显著不同之处。

Java虚拟机的另外一个显著特点就是实现了自动垃圾收集。在往常,写程序的时候要牢记对象之间的关联,假若在程序块中申请了对象空间,就必须在出口将其释放掉。而自动垃圾收集功能带给开发者的最大好处就是可以非常方便地从整体上把系统的对象组织成一张对象图,只需往这张图中添加对象,维护对象之间的关联,却不需要自己做复杂的清扫工作。正是这种思维单纯的对象图的支持,使得OR Mapping(关系数据库与对象映射)技术在最近得以广泛应用,设计模式也更容易被Java群体所接受。

1.5.2 虚拟机的优化

1995年,第一代JDK(Java Development Kit,Java开发工具包)出台之时,其虚拟机执行依靠字节码解释器(Byte Code Interceptor),也就是说,每条指令都由虚拟机来当场解释执行,这造成速度非常缓慢。有人开始总结“速度优化经验”,比如说尽量把所有代码都放在较大的方法中执行、少用接口等,这完全与Java语言的设计目的背道而驰,当时却是很多程序员津津乐道的经验之谈。原因无他,就是Java本身执行太慢了。

于是,Sun公司的工程师开始拼命想着提高执行速度。JIT静态编译器是1996年10月Sun公司公布的第一个编译器。JIT编译器在每段代码执行前进行编译,编译的结果为本地静态机器码,执行速度有了质的提高。Sun公司当时凭借其傲人的JIT编译器,在整个Java界受到热烈的追捧。1998年, Java 1.2发布时附带了JIT编译器,从此Java的使用者终于可以抛开“速度优化经验”了。

JIT静态编译器虽然可以解决一些问题,但是性能仍然和C/C++有很大的差距。对一段程序而言,一名优秀的程序员是如何来改进运行速度的呢?首先,他不会傻到把所有代码都优化,他会观察、思考到底哪段代码对整体性能影响最大,按照经验,整个程序10%~20%的代码会占据 80%~90%的运行时间,然后集中精力来优化这一段代码,就可以使整个程序的性能得到很大程度的优化。Hot Spot引擎就是模仿人工的这种方法进行优化的。在程序运行的开始,Java代码仍然解释执行,Hot Spot引擎开始进行采样(Profiling)。根据采样的结果确定哪段程序占用了较多的运行时间,就认为它是“Hot Spot”,它也就是目前程序的瓶颈。引擎会针对这段程序启动一个单独的线程进行优化。Hot Spot引擎不像原始的JIT编译器那样无差别地编译所有代码,而是集中精力对Hot Spot代码进行深度优化,使这部分代码执行起来更加迅捷,之前的静态编译器只能按照预定的策略进行编译优化,而Hot Spot引擎的优化是基于采样结果的,因此这种方法对所有应用程序都有效。1999年3月27日,Sun公司公布了第一个Hot Spot引擎。在随后的2000年5月公布的JDK 1.3中包含了Hot Sopt引擎,这也使JDK 1.3成了具有里程碑意义的发行版本。

Hot Spot代表的是一种动态编译技术。对Java这种大量使用委派、组合等面向对象特性的程序来说,动态编译相比静态编译有显著的优势,比如Method Inlining。方法的调用是很耗时的操作,假若可以把方法调用直接内嵌到调用者的代码中,就可以节省大量的时间,这就是Method Inlining。因为涉及类的重载,静态优化很难确切知道哪些属性和方法被重载,因此很难对Method进行合并,只好在方法内部进行静态编译,如果每个方法都很小,静态优化能起到的作用也就比较小。而动态编译因为可以随时掌握类的重载情况,就可以把相关的方法合并进行深度优化。现代的Java程序,特别是在设计模式教育得到普及之后,大量使用了类的继承、委派,形成了很多短小的方法,动态编译的优势就更加明显了。

继续进行优化的方法有几种:一是研究新的采样算法,因为采样关系到不同的优化策略,会对整体性能有比较大的影响;二是研究深度优化的方法;三是研究垃圾收集的算法。垃圾收集会使程序短暂的停顿,这会带来负面的用户体验,于是针对提高垃圾收集的效率和减少延迟,出现了五花八门的算法,如渐进式收集、火车算法等。在多处理器的时候,如何利用多处理器进行并行收集也是研究的一个热点,这方面,BEA的JRocket走在了前面。

1.5.3 现实生活中的虚拟机

本小节将介绍目前市面上可见的各种虚拟机。

首先要提到的,毫无疑问是Sun公司的虚拟机。作为大众心目中的“官方实现”,Sun公司拥有最大的用户群,并且拥有“兼容基准”的地位,其他虚拟机都必须考虑和Sun公司虚拟机的兼容性问题。比如,JRocket在某些特殊情况下表现出和Sun公司产品不同的特性,可能对程序运行有影响。不过Sun公司也的确没有让广大用户失望,虽然早期性能比不上Symantec,后来1.2版本的性能又被IBM超越,但Sun公司一直在努力革新,特别是 1.4.2版本之后,性能有了长足的进步。虽然JDK 1.5的虚拟机在性能上没有什么提高,但是增强了稳定性,据说修改了8000处Bug。

其次是IBM。IBM的JDK在1.3版本时代创下了最好的性能记录,从此树立了高端形象,特别是Web Sphere产品,得到了很好的评价。其JDK也是最早支持64bit的JDK之一。直到现在,IBM JDK在高端领域仍然是和BEA有一拼的。

然后是后起之秀BEA的JRocket。BEA在Java虚拟机领域异军突起,不免让人有些瞠目,不过它采取的战略特别简单:自己没有,索性花钱购买在此领域深有研究的JRocket,再在前面加上BEA的标志。JRocket瞄准高端服务器市场,在多处理器环境下有不俗的表现。

除此之外,还有几个开放源代码的Java虚拟机值得一提。首先就是大名鼎鼎的Jikes RVM。说起其大名,大多数人都知道Jikes编译器是 IBM开发的,效率比同时代的Javac编译器高得多,很多开发者都使用Jikes编译器来取代Javac。Jikes RVM是IBM开源出来的一整套虚拟机技术,包含了JIT、GC 的完整实现,在其网站上也有众多论文,实在是深入研究 Java 虚拟机的绝佳资源(http://jikesrvm.sourceforge.net)。

Kaffe是一个老牌的Java虚拟机,不过现在已经很少听到了。

GNU有GCJ和GNU classpath两个计划。GNU classpath是一个底层实现,而GCJ是支持Java的预编译器。

时光流转,轰轰烈烈的Java虚拟机性能争论仿佛还在持续之中,新的争论却已经提升到了“Java的性能是否已经超越C/C++”。Joakim Dahlstedt 是 JRockit 的主要架构设计师之一。他坚持认为, Java绝不是一种速度慢、效率低的语言,Java虚拟机是一个关键的组件,确保了系统的部署与运行和开发一样快速、轻松。特别是目前的开发趋势是采用大量预制的框架,动态编译有可能比C/C++这样的静态优化获得更好的性能。