首页 | 源码下载 | 网站模板 | 网页特效 | 广告代码 | 网页素材 | 字体下载 | 书库 | 站长工具
会员投稿 投稿指南 RSS订阅
当前位置:主页>网络编程>java教程>资讯:使用实时Java降低Java应用程序的易变性(3)

使用实时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详解

评论总数:0 [ 查看全部 ] 网友评论


关于我们隐私版权广告服务友情链接联系我们网站地图