栏目导航
热点推荐
- 三十条有用的 Java 编程规则
- Java制作水印图片源码
- Java常见异常及可能的导致原因
- Java中的修饰词使用方法总结
- J2EE系统异常的处理准则
- Java中的异常、断言、日志解析(
- Java面试技巧:Java面试题集锦(
- 面向Java开发人员的Scala指南:
- Java程序员:一刻钟精通正则表达
- 网友经验分享:学好java开发的关
- 专家解答:创建表格与数据库进行
- Java远程访问Domino数据库
阅览排行
使用实时Java降低Java应用程序的易变性(3)
www.jz123.cn 2010-10-22 来源: 中国建站 责任编辑(袁袁) 我要投递新闻
如果我们收集完成每个 TaskHandler.run() 调用的时间统计信息,我们可以看到 JVM 和应用程序设计引入了多少易变性。我们使用具有 8 个物理核心的 IBM xServer e5440,安装了 Red Hat RHEL MRG 实时操作系统。(禁用了超线程。注意,尽管超线程可以在基准测试中提供一些吞吐量改进,但是由于其虚拟核心并不完整,所以在启用超线程的处理器上的操作的物理核心性能可能具有明显不同的计时)。当在 8 核心机器上使用 IBM Java6 SR3 JVM 和 6 个线程运行此服务器时(我们将一个核心保留给 Server 主线程,另一个核心供 GCStressorThread 使用),我们得到了以下(代表性的)结果:
$ java -Xms700m -Xmx700m -Xgcpolicy:optthruput Server 6 10000 operations in 16582 ms Throughput is 603 operations / second Histogram of operation times: 9ms - 10ms 9942 99 % 10ms - 11ms 2 0 % 11ms - 12ms 32 0 % 30ms - 40ms 4 0 % 70ms - 80ms 1 0 % 200ms - 300ms 6 0 % 400ms - 500ms 6 0 % 500ms - 542ms 6 0 % |
可以看到,几乎所有操作都在 10 毫秒内完成,但是一些操作花了超过半秒(长 50 倍)的时间。这个差异太大了!我们看看如何消除 Java 加载、JIT 本机代码编译、GC 和线程导致的延迟,从而消除这种易变性。
我们首先通过 -verbose:class 完整地运行应用程序,收集应用程序加载的类列表。我们将输出存储到一个文件,然后修改它,使该文件的每行上都具有一个格式正确的名称。我们将一个 preload() 方法包含到 Server 类中,以加载每个类,JIT 编译这些类的所有方法,然后禁用 JIT 编译器,如清单 9 所示:
清单 9. 预加载服务器的类和方法
private void preload(String classesFileName) { try { FileReader fReader = new FileReader(classesFileName); BufferedReader reader = new BufferedReader(fReader); String className = reader.readLine(); while (className != null) { try { Class clazz = Class.forName(className); String n = clazz.getName(); Compiler.compileClass(clazz); } catch (Exception e) { } className = reader.readLine(); } } catch (Exception e) { } Compiler.disable(); } |
在这个简单的服务器中,类加载并不是一个重大问题,因为 TaskHandler.run() 方法非常简单:一旦加载该类,就不会在以后执行 Server 时发生太多类加载操作,这可以通过运行 -verbose:class 来验证。主要的优点源自于在运行任何测试 TaskHandler 操作之前运行方法。尽管我们可以使用预备循环,但此方法是特定于 JVM 的,因为 JIT 编译器用于选择要编译方法的启发机制在各个 JVM 实现之间有所不同。使用 Compiler.compile() 服务器会提供更加可控的编译行为,但正如本文前面提到的,使用此方法应该会使吞吐量下降。使用这些选项运行应用程序的结果如下:
$ java -Xms700m -Xmx700m -Xgcpolicy:optthruput Server 6 10000 operations in 20936 ms Throughput is 477 operations / second Histogram of operation times: 11ms - 12ms 9509 95 % 12ms - 13ms 478 4 % 13ms - 14ms 1 0 % 400ms - 500ms 6 0 % 500ms - 527ms 6 0 % |
注意,尽管最长的延迟没有多大变化,但直方图比最初小多了。JTI 编译器明确引入了许多较短的延迟,所以较早执行编译,然后禁用 JIT 编译器显然是一大进步。另一个有趣的结果是,普通操作时间变得更长了(从 9 - 10 毫秒增加到了 11 - 12 毫秒)。操作变慢了,原因在于在调用方法之前强制执行 JIT 编译所生成的代码质量比完整编译的代码质量要低很多。这个结果并不奇怪,因为 JIT 编译器的一个最大优势是利用应用程序的动态特征来使其更高效地运行。
上一篇:使用实时Java降低Java应用程序的易变性(2) 下一篇:Java5:BigInteger、BigDecimal详解