IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Java technology  >

Java 技术,IBM 风格: IBM Developer Kit 简介

概述 Java 5.0 的 IBM 端口中的新功能和新特性

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

讨论


级别: 初级

Chris Bailey (baileyc@uk.ibm.com), 咨询软件工程师, IBM

2006 年 5 月 25 日

随着 Java™ 5.0 的出现,IBM® 在它的 Java 技术实现方面进行了改进。本文是一个分 5 部分的文章系列的第一篇,这个系列主要关注 IBM Developer Kit 最新版本中的变化。作者 Chris Bailey 首先简要描述了 Java 5.0 平台的改进,然后介绍了 IBM SDK 实现中的变化。

针对 Java 平台 5.0 版本的 IBM Developer Kit 标志着显著的进步,它在语言特性和底层执行技术方面有重大改进。本文是一个分 5 部分的文章系列的第一篇,概述了 IBM 对它的虚拟机技术所做的一些主要改变和改进,包括传统的垃圾收集、共享类数据,以及在监视和调试工具及 API 方面的改进。但是,在讨论 IBM 实现的改进之前,我们先看看 Java 5.0 本身的改进。

关于本系列

Java 技术,IBM 风格 系列介绍 Java 平台的 IBM 实现的最新版本。您将了解 IBM 如何实现 Java 平台 5.0 版本中的一些改进,以及如何使用 IBM 在新版本中提供的一些增值特性。

如果想对文章发表评论或提问题,请与相应的作者联系。要对这个系列的整体发表评论,可以联系系列负责人 Chris Bailey。关于这里讨论的概念的更多信息以及下载 IBM 最新版本的链接,请参见 参考资料

Java 5.0 的改进

自从引入 Java 2 平台以来,Java 2 Standard Edition(J2SE 5.0)在 Java Class Library(JCL)API 和 Java 虚拟机(Java Virtual Machine,JVM)规范中引入了许多特性改进。这些特性在所有 Java 技术实现厂商的所有 5.0 实现中都可用了。它们主要涉及两个领域:开发的简化以及监视和管理。

简化开发的特性

5.0 版本中的简化开发特性的设计目的是,让开发人员能够用更少的代码建立简单构造,以及提供更多的编译时检查,从而帮助开发人员在开发周期中更早地发现问题。下面是对这些特性的简要介绍:

  • 用泛型提供编译时类型安全性:泛型与 C++ 模板相似。一般的(即泛型(generic))类独立于具体的类型,在实例化时通过使用参数化类型(parameterized type)提供类型安全性。结合使用参数化类型和泛型类就可以进行编译时类型安全性检查,Java 5.0 平台中的集合类使用了这种方法。

  • 扩展的 for 循环:这个新的语言构造与其他语言中的 for each 循环相似,它简化了循环遍历集合和数组的过程,因为不再需要使用显式定义的迭代器和索引变量。

  • 原生类型的自动装箱:这个特性简化了将原生类型插入集合对象的过程,因为不再需要将 Java 原生类型(比如 int装箱(box) 成对应的包装器类(比如 java.lang.Integer),在删除它们时也不需要开箱(unbox)

  • 类型安全的枚举:这个特性引入了 Java 语言对枚举类型的支持,提供了比使用静态 final 声明更强大且类型安全的解决方案。

  • 支持导入常量:这个特性使静态方法和字段能够被导入,这样在访问静态成员时就不必使用完全限定的类名。

  • Java Language Metadata(标注):这个特性允许开发人员将标注(annotation) 添加到代码中。标注作为修饰符,可以添加到包、类、接口、方法或字段声明中。此信息存储在源代码文件和类文件中,工具和 Java 应用程序可以通过 Java Reflection API 获得它。用于文档编制、编译器检查和代码分析的工具可以使用这些额外信息。

  • 并发工具:这个特性为开发并发类提供了基本构造块,包括线程池和线程安全的集合,并引入了低级锁定原语,包括信号量和原子性变量。

监视和管理特性

J2SE 新的监视和管理特性的设计目的是简化对 Java 运行时的状态的监视。可以使用监视和管理 API 从 Java 代码调用这些功能,或者使用 JVM Tools Interface(JVMTI)从 C 代码调用:

  • 监视和管理 API:这个特性使 Java 程序或远程代理能够监视虚拟机的 “健康状态” 并观察其他系统级的活动和事件。可以利用这些特性开发自治和自适应系统。

  • JVM Tools Interface:JVMTI 是一种更轻量的、灵活的 JVM Profiling Interface(JVMPI)替代品,它是一个基于 C 的接口,用于编写开发时和运行时监视工具。




回页首


来自 IBM 的增值改进:概述

通过 Java 编译器、JCL API 和 JVM 规范在 5.0 中添加的规范和 API 改进影响了 Java 平台的所有新实现;另外,允许 Java 厂商在自己的 Java 实现中开发和提供自己的增值改进。IBM 以两种形式提供自己的改进:IBM 开发的 Java 语言扩展和 Java 运行时环境的 IBM 实现中的改进。

Java 语言扩展

IBM 的增值 Java 语言扩展包括三个主要组件:对象请求代理(object request broker,ORB)、XML 和 安全性。这三个组件是 IBM 提供的代码,提供了顾客和某些 IBM 产品需要的特性:

  • ORB:IBM 发起了 RMI-IIOP 的开发并将其包含到 J2SE 中,作为 RMI-JRMP 的替代品。在此之后,IBM 继续开发自己的实现,来满足顾客需求并确保版本之间的互操作性,尤其是对于 WebSphere Application Server。

  • XML 和 XSLT:IBM 基于 Apache Xerces Java 和 Xalan Java 开放源码项目(IBM 是这个项目的主要捐献者)开发了一个 XML/XLST 规范实现。IBM 包包含在 Java 5.0 规范中没有指定的 XML API,以及 Xerces Native Interface 和 XML Schema API。

  • 安全性:IBM 通过标准 Java API 提供了广泛的安全服务。安全性组件包含各种安全性算法和机制的 IBM 实现。除了基本的安全性提供者之外,IBM 还为密码术硬件提供了 FIPS 支持。另外,提供了 iKeyman 实用程序来管理密钥和证书。

Java 运行时改进

Java 运行时的 IBM 实现针对 5.0 版进行了大量开发工作,影响到所有三个主要运行时组件:虚拟机(virtual machine,VM)、垃圾收集器(garbage collector,GC)和即时(just-in-time,JIT)编译器。这些努力有两个主要目的:提高应用程序的执行性能,以及提高可靠性、可用性和可伸缩性(RAS)。

实现这些改进的方式有两种:转移到所有 Java 运行时风格(Micro Edition(ME)、Standard Edition(SE)和 Enterprise Edition(EE))的通用代码基,IBM 为这些风格产生了端口;在这三个组件中分别引入改进。

在本文的其余部分和本系列的后续文章中,我们将详细讨论所有与 Java 运行时相关的改变。





回页首


通用代码基

IBM 长期以来一直为 Java 平台的所有三个版本开发实现。自 J2SE 5.0 发布以来,Java 运行时的 IBM 实现的所有底层组件都是在一个通用代码基上构建的。

通用代码基是使用一个框架引擎系统和可插入配置构造的,这可以支持最大程度的代码共享,同时可以迎合每个 Java 版本需要的任何功能差异。这改进了 J2SE IBM 实现的内存占用量、启动时间和性能,并改进了 J2ME IBM 实现的可伸缩性和可服务性。由于对相同的通用代码进行了广泛深入的测试,各个版本都由此受益了,因此改进了可靠性和稳定性。

Java 堆栈和线程寄存器中的值

Java 堆栈中的值是相对的,因为堆栈上的帧包含 Java 方法的局部变量。线程寄存器中的值也是相对的,因为这里存储当前正在执行的方法的局部变量和操作数。

垃圾收集器改进

除了利用可插入配置转移到通用的垃圾收集框架之外,对于 GC 组件还有 4 项主要改进:从保守性收集器转移到类型精确的收集器,引入了一个并行收集器,引入了分代的并发收集器,重新设计了详细 GC 日志记录设施。

类型精确的收集器

以前的 J2SE IBM 实现实现包含一个保守性(conservative) 垃圾收集器。这种收集器假设线程的 Java 堆栈或线程寄存器中的每个值都可能包含对 Java 对象的引用,因此称为保守性的。

这意味着垃圾收集器必须追踪每个值,并判断它是否指向 Java 堆上的对象,这可能导致将未被引用的对象标为被引用的 —— Java 堆栈或寄存器中的值可能实际上只是一个 long 值,但是这个值碰巧指向一个有对象存在的位置。在这种情况下,就会发生所谓的保留垃圾(retained garbage) 的情况,因为实际上已经不再需要的对象却在垃圾收集过程中保留下来了。这会使应用程序占用的内存比真正需要的内存量大。

将对象分配到碎片化的堆中

Java 对象要求创建一个单一的连续的内存区。如果 Java 堆有残留的碎片,那么可能没有足够的连续内存可以分配给给定对象,即使总内存是足够的。即使在内存看起来足够的情况下,这也会导致 OutOfMemoryError

使用保守性收集器的另一个影响是需要对 Java 对象进行 pindose。当从 JNI(本机)代码引用对象时,它们就被 pin。当从 Java 堆栈或寄存器引用对象(有意或无意地成为保留的垃圾)时,对象就成为 dose 的。在垃圾收集器对 Java 对象进行紧凑排列期间,pin 和 dose 会阻止对象在 Java 堆上移动。这是因为在对象移动时,Java 堆栈和寄存器中的对象引用无法更新为对象的新位置;对于保留的垃圾,它实际上会变成一个不引用 Java 对象的值。

在垃圾收集的紧凑排列阶段,无法移动这些对象,这造成无法完全消除 Java 堆上对象的碎片化,因此留下了残留的碎片。这个问题造成在 Java 堆上即使看起来有足够的空闲内存,也无法分配对象,同时导致运行应用程序所需的 Java 堆比真正需要的大。

这两个问题都通过转移到类型精确的收集器(type-accurate collector) 解决了,这种收集器维护自己的经过良好描述的类型精确的堆栈。这避免了保留垃圾的问题,因为它知道一个值是否引用 Java 对象。因为能够编辑这些类型精确的堆栈,所以在紧凑排列期间对象可以移动了,因而消除了残留碎片问题。

并行收集器

紧凑排列是运行垃圾收集时最消耗时间的阶段。为了减少紧凑排列的时间,在 Java 5.0 技术的 IBM 实现中引入了并行紧凑排列(parallel compaction)

并行紧凑排列允许多个线程帮助在 Java 堆上移动对象,从而将大量的小块空闲空间合并成少量的大块空间。实现的方法是,对于进程可以使用的每个 CPU,建立一个线程,并将 Java 堆分割成许多名义上的区域,这些线程分别负责每个区域的紧凑排列。

分代的并发收集器

分代并发(或称为 gencon)收集器利用了 “大多数对象在年轻时就死了” 这一并不可靠的假设,通过创建分成两代的 Java 堆来实现。通过将对象分成新老两代,收集过程可以主要关注年轻的对象。年轻(也称为婴儿(nursery)) 代使用一个半空间复制收集器,而老年(也称为长存的(tenured)) 代使用并发的标志扫描收集器。

消息 GC 日志记录更新

为了改进 GC 组件的 RAS 品质,GC 日志记录机制在两个方面进行了更新。日志记录器提供更详细的信息并采用 XML 格式而不是一般文本;由于第一次数据捕捉失败,所以它还将此数据的子集记录进内存中的缓冲区。

详细 GC 输出转移到基于 XML 的结构,因此更容易通过简单的 XML 读取器(比如 Web 浏览器)查看数据,也更容易通过各种详细 GC 分析工具进行分析。

内存中的跟踪缓冲区在发生失败时或者应用户的请求存储到文件,所以即使详细 GC 没有启用,它也可以提供关于 Java 内存使用情况和 GC 状态的基本信息,而且它大大提高了第一次失败数据捕捉信息的质量。





回页首


对 JIT 编译器的改进

尽管 Java 平台的用户对 JIT 编译器的内部不了解,但是它对 Java 运行时的应用程序执行性能影响很大。它在运行时将 Java 字节码转换为优化的机器码,从而大大提高了 Java 方法的运行速度。

从 1.4.2 到 5.0 版,许多改进和改变提高了 IBM JIT 编译器的性能,同时减小了 JIT 编译对正在运行的应用程序的影响。表 1 列出了主要的变化:


表 1. IBM JIT 编译器中的改进
1.4.25.0
在正在执行 Java 方法的 Java 线程上同步地编译使用单独的异步编译线程
在需要时对方法进行编译方法排队进行编译
对于 Java 方法,使用本机堆栈维护一个单独的 Java 堆栈
只有一种编译优化共有 5 个编译优化级别
方法只能编译一次可以进行重新编译
可能出现包含循环的堆栈上方法替换没有堆栈上替换
当应用 -Xdebug 时,禁用 JIT在调试模式中,JIT 继续采用降低的优化级别进行编译

在这些改进中,主要的新特性是异步编译、多级优化和分析驱动的重新编译。

异步编译

Java 方法的 JIT 编译现在在一个独立于进行调用的线程的专用线程上异步地执行。这意味着,调用某个 Java 方法从而触发编译的线程在编译期间不再被阻塞。方法被添加到编译队列中,而线程可以继续运行并执行方法的非编译版本。方法编译之后,对方法的下一次调用会运行 JIT 编译的版本。为了确保频繁使用的方法优先编译,队列常常会重新调整次序并重新确定优先级。

在多处理器计算机上,异步编译可以大大提高启动时的性能,因为应用程序启动阶段的大部分是单线程的,而 Java 方法的编译由一个单独的处理器承担。

多级优化

现在,JIT 编译器能够采用 5 个优化级别之一对 Java 方法进行编译。通过使用单独的采样线程,编译器判断特定的 Java 方法花费多长时间,由此判断这个方法是否需要 JIT 编译,以及应该采用哪个优化级别进行编译。最常执行的 Java 方法采用最高的优化级别进行编译,这会获得最大的性能收益,但是完成编译所花费的时间成本也最大,内存需求也可能更高。不经常使用的方法采用较低的优化级别进行编译,这样编译完成得很快并产生显著的性能收益。

分析驱动的重新编译

如果由于应用程序运行方式的改变,Java 方法需要以更高的优化级别进行编译,那么 JIT 编译器能够对它们进行重新编译。在最高优化级别上,根据从正在执行的代码自动生成的动态分析数据执行重新编译。这些信息反映了方法本身实际上是如何被使用的。在收集短期数据之后,使用此数据对方法进行重新编译并相应地优化。





回页首


虚拟机改进

在 Java 5.0 技术的 IBM 实现中,与其他组件一样,虚拟机也得到了大量改进。两个最重要的改进是共享类的实现以及分析和调试方面的新特性。

共享类

共享类原来只在 z/OS 和 OS/390 的 Java 平台 IBM 实现中可用。共享类的这种实现已经被废弃,替换为所有平台上的新实现。

新的实现在共享内存中维护一个静态类数据缓存,它可以在实现新的共享类功能的所有 IBM Java 运行时之间共享;这个缓存在 Java 运行时的调用之间持续存在。共享类功能应用于所有 JCL 和基于类路径的类,而且通过使用简单的 API 很容易应用于定制类装载器装载的类。在缓存已经填充之后,这种功能可以减少内存占用和启动时间。对于在一台计算机上有多个 Java 运行时,或者运行时常常重新启动的情况,这个特性尤其有效。

分析和调试

除了支持 Java Debug Wire Protocol(JDWP)、JVM Profiling Interface(JVMPI)和 JVM Tools Interface(JVMTI)之外,IBM 调试器实现还有另外两个特性:高速调试(high-speed debug)热代码替换(hot-code replace)

高速调试允许在运行 Java 调试器的同时执行 Java 方法的 JIT 编译,而且这种编译可以采用几乎完全的优化。这在调试大型 Java 应用程序时很有用,例如那些在 J2EE 堆栈上运行的应用程序;在这些应用程序中如果禁用 JIT,那么调试时的性能会很差。

热代码替换允许在调试器下动态地修改源代码,并立即运行新代码而不需要重新启动应用程序。

能够在调试器下几乎全速地运行代码并动态地修改代码,这大大提高了应用程序开发的效率。





回页首


可靠性、可用性和可服务性改进

对于对 Java 应用程序和 Java 运行时本身进行监视和故障调试,Java 平台的 IBM 实现已经提供了基础设施和工具。Java 运行时中已经添加了许多这方面的改进,包括写入内部缓冲区的连续低级跟踪(作为第一次失败数据捕捉的辅助措施)、一个用于监视本机内存使用情况的新选项和一个强大的 JNI 代码检验器,并对转储和跟踪引擎进行了重新设计。5.0 的实现还添加了一个基于 Java 的工具 API,用于查询系统转储文件,这使工具开发人员能够访问转储中关于对象、线程、锁等的信息,而不需要掌握 JVM 的内部结构。

跟踪引擎

跟踪引擎已经重新改造过了,并添加了一个内部 “flight recorder”。它连续地将关键的 VM 和 JCL 跟踪点写入每个线程专用的回绕型缓冲区。GC 数据也写入一个单独的回绕型缓冲区,从而确保可以很容易地获得此数据,并提供一定的 GC 历史数据。

除了 VM 的内部跟踪之外,方法跟踪功能能够跟踪 Java 代码(包括 JCL 提供的代码和应用程序代码)的进入方法和退出方法事件。这不需要对应用程序代码做任何修改,它会提供时间戳、线程 ID 和参数信息。

转储引擎

通过对转储引擎的重构,可以触发转储的事件数量从 3 个增加到了 14 个。现在,可以在发生许多事件时生成转储,比如停止 VM、装载和卸载类、启动和停止线程、GC 周期以及抛出/捕获/未捕获异常。这些新功能与触发事件的能力相结合,就能够非常灵活地控制 Java 运行时何时创建转储以及创建什么类型的转储。

DTFJ Tooling API

Dump Toolkit and Framework for Java(DTFJ)是一个基于 Java 的 API,用来访问 Java 进程的系统转储中的事后信息。这使工具开发人员能够访问转储中关于系统、进程、Java VM 和 Java 应用程序的信息,而不需要了解相关结构在内存中是如何布局的。这样就能够编写更好的事后分析工具。





回页首


结束语

IBM Developer Kit for Java 5.0 引入了大量特性和功能。其中一些改进(包括性能和可靠性改进)会在从以前版本迁移到 5.0 时透明地提供给用户,其他改进需要调用。在本系列以后的几期中,我们将深入讨论 IBM 提供的一些增值改进的技术,包括垃圾收集策略、共享类和调试特性,以及如何利用它们。



参考资料

学习

获得产品和技术

讨论


关于作者

Chris Bailey

Chris Bailey 于 2000 年作为 Southampton University 的毕业生加入 IBM Java Technology Centre。他帮助用户解决在 Java 技术的 IBM 端口和基于 Java 平台的产品方面遇到的问题。Chris 主持着 developerWorks 论坛 “IBM Java Runtimes and SDKs”,当前主要关注改进提供给 Java 平台 IBM 端口用户的信息和工具的质量。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

将您的建议发给我们或者通过参加讨论与其他人分享您的想法.




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款