本文是“ 用实时Java开发”系列的第三篇也是最后一部分,展示了如何设计,编写,验证和分析基本的实时应用程序。 我们将说明:

  • 应用程序的时间和性能要求。
  • 为什么传统的非实时Java不适合该应用程序。
  • 选择了哪些实时Java编程技术。
  • 实现确定性的注意事项。
  • 测试和验证应用程序的确定性。
  • 用于调试实时确定性问题的工具和技术。
  • 改善可预测性的选项。

我们将应用程序设计和代码保持简单,以便我们可以专注于这些目标。 完整的源代码可供下载 。

演示应用

我们的任务是创建一个Java应用程序,使其在Linux®操作系统上运行,从而以较低的延迟从工业设备提供温度读数。 为了从Java语言的生产力和可移植性中受益,应将代码保持与标准非实时Java尽可能接近。 为了保持设备的效率,必须读取读数并将其传送到用户程序,然后由用户程序控制设备的进料和冷却速率。 要求在5毫秒内提供温度读数。 即使是短暂的延迟,也可能导致进料和冷却速度不理想,从而降低了设备的效率。

以极低的延迟定期将数据传递给消费者的范例是实时系统中的一种常见现象。 这些数据很可能只是股票市场价格或雷达信号。 第2部分描述了许多无法使用常规Java代码编写并且仍然满足这些临时要求的原因。 表1总结了这些原因,并列出了实时Java中可用的相应解决方案。 (并非所有实现都提供相同的解决方案集。)

表1.常规Java与实时Java
常规Java中的问题 实时Java解决方案
类加载延迟 通过预加载所需的类可以避免延迟。
即时(JIT)编译延迟 可以通过提前(AOT)编译或异步JIT编译来避免延迟。
在操作系统级别对线程优先级的支持非常有限 Java实时规范(RTSJ)确保至少有28个优先级可用。
不支持对非默认线程策略的控制 RTSJ为程序员提供了选择的选项-例如,先进先出(FIFO)。
长时间的垃圾收集(GC)延迟

RTSJ为NoHeapRealtimeThread (NHRT)应用程序提供了不受NoHeapRealtimeThread暂停影响的非堆存储区域(范围和不朽的)。

现在可以使用实时垃圾收集器来减少GC到应用程序线程的延迟。

操作系统内核线程延迟和优先级反转 实时内核(例如,实时Linux发行版中提供的内核)经过精心设计,可以避免长时间运行的内核线程,被完全抢占并避免优先级倒置(在第1部分中进行了介绍)。

哪种实时Java编程模型?

演示应用程序的代码应尽可能接近标准非实时Java的要求对我们选择RTSJ编程模型产生了直接的后果:使用NHRT和内存范围来避免GC延迟不是一种选择。

通过使用RealtimeThread ,我们可以访问实时Java编程扩展,而无需进行NHRT编程的复杂性。 使用NHRT会迫使程序员控制内存,而不是依靠常规的自动GC。 实际上,NHRT必须使用内存作用域而不是不朽内存,因为不朽内存是有限的,最终将耗尽。 哪些类是“ NHRT安全”的,也有一些限制,即,不要在主堆上使用或引用对象,而NHRT则不能。

除了更简单的编程,我们还可以期望:

  • 更好的性能。 由于非堆对象需要内存屏障和其他开销,因此堆对象比永生对象或作用域对象创建和引用的速度更快。
  • 更容易调试。 必须防止NHRT引用堆内存,这需要调试内存访问异常。 NHRT的内存不足故障也可以从不朽堆或内存范围之一触发。

哪个实时JVM?

有两个IBM®JVM Java 6软件包可用于Linux上的实时程序,它们的名称相似,但功能和性能/确定性的取舍却有所不同(请参阅参考资料 )。 运行本文中的演示程序所需的一个是用于RT Linux的IBMWebSphere®Real Time。 它提供了RTSJ功能,例如RealtimeThread和周期计时器类,我们将需要它们来实现温度读取线程,并且它提供了最短的GC暂停。 它在实时Linux内核和特定硬件上运行,以提供硬实时应用程序所需的确定性。 (另一个软件包– IBM WebSphere Real Time for Linux –支持软实时应用程序,提供更高的吞吐量和可伸缩性。它不包括RTSJ编程库。它确实包括了Metronome垃圾收集器,其目标是将GC暂停时间限制在大约3毫秒。)

演示应用程序设计

使用RealtimeThread而不是NHRT的主要缺点是线程将受到实时垃圾收集器的(短暂)暂停。 使用WebSphere Real Time的Metronome垃圾收集器,我们可以依靠仅约500微秒的GC暂停来暂停应用程序代码。 而且我们知道,在GC周期内,我们的应用程序将保证至少有70%的时间运行。 换句话说,在一个10毫秒的窗口中,我们期望每个应用程序的时间不超过6个GC量子,每个量子约为500微秒。 本文稍后将说明此模型。

此应用程序的要求是以不超过5毫秒的间隔提供温度数据。 设计是每2毫秒读取一次温度,即使GC循环运行,也要有足够的应变来满足要求。

实时服务质量的定义

实时服务质量的概念突出了确定性需求与纯性能需求之间的对比。 对于实时应用程序,我们通常希望确保系统行为的准确性和及时性,而不是吞吐率。 实际上,由于支持RTSJ所需的开销(例如内存屏障)以及需要禁用会产生不确定性行为的运行时优化,因此实时Java实现将比同类标准Java实现慢。 根据我们定义的是硬实时系统还是软实时系统,需求的表达方式可能有所不同。

在硬实时系统中,系统必须在5毫秒内将所有温度数据提供给使用者线程。 超过5毫秒的任何延迟都将构成系统故障。 在软实时系统中,系统必须在5毫秒内将几乎所有温度数据提供给使用者线程。 如果在5毫秒内至少有99.9%的读数可用,而在200毫秒内有99.999%的读数可用,则该系统是可以接受的。

验证实时系统

实时应用程序的简单功能或性能测试不足以证明它们满足要求。 在一些测试中正常运行的系统可能会随时间降级或以其他方式更改其输出。 除非长期进行大量采样,否则不会显示执行时间或定期调度的可能值范围。

例如,根据先前的软实时要求,我们知道必须至少进行100,000次测试,才能开始显示满足200毫秒内99.999%的目标。 对于严格的实时要求,系统开发人员有责任进行足够的测试,以使他们能够证明系统将满足在5毫秒内将所有温度数据提供给使用者线程的要求。 这通常是通过长时间测试与性能数据分布的统计检查相结合来完成的。 存在用于创建与数据样本的最佳拟合分布曲线的工具,并且建模数据可用于预测极端离群值的数量和可能性。 因为我们通常没有足够的时间来测试系统,因为它们可能无法部署,所以在最坏情况下观察到的异常情况之外采取应急措施是定义认证系统的实用方法。

演示应用程序的实现

为简单起见,我们将仅考虑传递温度数据的线程使用的代码。 数据消费线程没有详细描述; 可以假定它们在同一台计算机上的不同进程中运行。

对于最初的实现,除了使用WebSphere Real Time AOT编译工具admincache之外,我们不会尝试最大化确定性。 重点是确定不确定性剩余领域的技术和工具,以及解决这些问题的方法。

我们使用Reader进程每2毫秒轮询一次设备的温度传感器(在此由随机数发生器模拟)。 javax.realtime.PeriodicTimer类是RTSJ的用于定期执行操作的解决方案,并且Reader使用PeriodicTimer轮询温度传感器。 这被实现为BoundAsyncEventHandler以实现最低延迟。 读取器为每次读取构造XML片段,并将其写入连接到Writer进程的网络套接字。 如果读取传感器和写入数据的周期所花费的时间超过2毫秒,则会报告错误。

Writer进程与Reader进程在同一台计算机上的单独JVM中运行,并在网络套接字上侦听温度读数。 编写器使用javax.realtime.RealtimeThread侦听网络套接字,以利用FIFO调度模型和细粒度的优先级控制。 它解压缩XML代码片段,提取温度读数,并将其写入日志文件。 为了使数据能够立即提供给消费者,Writer还具有3毫秒的截止日期,可以将任何测量结果写入磁盘。 如果读取温度和写入数据之间的总时间超过5毫秒,则会报告错误。

图1显示了应用程序的数据流:

图1.数据流

请注意,在对时间敏感的情况下,将XML用于如此少量的数据并不是良好的设计习惯。 演示应用程序的构建旨在为探索WebSphere Real Time性能提供有用的示例。 不应将其作为良好的远程温度监控应用程序的示例。

运行演示

如果您具有WebSphere Real Time环境,则可以自己运行演示:

  1. 在WebSphere Real Time机器上的某个位置解压缩演示源。
  2. 使用以下命令编译演示:
    PATH to WRT/bin/javac -Xrealtime *.java
  3. 传递端口号和日志文件名,启动Writer进程。 例如:
    sdk/jre/bin/java -Xrealtime Writer 8080 readings.txt
  4. 启动Reader进程,并传递Writer进程的主机名和端口号。 例如:
    PATH to WRT/jre/bin/java -Xrealtime Reader localhost 8080

演示输出

该代码报告每个Reader和Writer线程中的延迟。 写入器还报告在读取温度和写入磁盘之间的总传输时间。 为了达到我们的实时目标,此传输时间不得(永远)超过5毫秒。 对于软实时,在5毫秒内满足99.9%的要求的第一部分,在200毫秒内满足99.999%的要求的第二部分。

在运行该应用程序的IBM测试系统上,达到了软实时目标,但是针对硬实时目标存在故障。 下一节将讨论一些可用于调查这些故障的工具和技术。

分析实时Java程序

实时编程中使用的时间限制通常比用于常规Java分析的时间限制要严格得多。 实时Java的处理时间通常在纳秒到毫秒的范围内,而普通的Java编程则为数十毫秒或以上。 要了解演示应用程序的故障,我们需要在微秒和低毫秒范围内工作。

我们注意到在运行初期,演示失败最常见。 这在实时系统和其他系统中都是常见的发现:最慢的运行往往是前几个。 我们已经谈到了造成这种情况的一些原因:类加载和JIT编译。

跟踪类加载

在最简单的级别上,我们可以使用-verbose:class命令行选项运行该应用程序,以输出有关所有类加载事件的信息。 但是,要将任何异常值与其他活动(例如类加载)相关联,我们需要针对异常事件和可疑原因的准确时间戳。 出于一般目的,我们可以使用Java虚拟机工具接口(JVMTI)类装入事件自行编写工具(请参阅参考资料 ),但是我们仍然需要检测应用程序代码并关联带时间戳的事件。

跟踪JIT编译活动

现代的JIT编译器具有很大的性能优势。 它们是技术上高度复杂的软件,大多数开发人员都将其视为黑匣子。 例如,与配置垃圾收集器相比,通过配置选项进行的显式交互当然更少,并且对JIT活动的可视性更少。 但是,我们可以通过命令行选项启用一些JIT详细信息,并且与JIT相关的JVMTI事件可用于跟踪JIT代码生成的工具。

从命令行,我们可以使用-Xjit:compiling标志启动JVM,以通知方法编译(以及重新编译到更高的优化级别)。

检测演示代码

假设我们已经启用了verbose:class-Xjit:compiling ,但是在所有类加载完成并且JIT生成的代码稳定之后,我们看到了演示计时失败。 在这种情况下,我们需要进一步探究我们的应用程序相对于其他JVM活动到底在做什么。

一种方法是使用时间戳标记代码,以识别主要延迟发生的位置。 实时Java平台的一个优势是我们可以使用高度准确的高性能时钟。 我们可以在Reader代码中将以下代码添加到我们的可疑区域:

AbsoluteTime startTime1 = clock.getTime();
xmlSnippet.append("<reading><sensor id=\"");
AbsoluteTime startTime2 = clock.getTime();
RelativeTime timeTaken = startTime2.subtract(startTime1);
System.err.println("Time taken: " + timeTaken);

这可以识别慢速的代码行,但这是一个费力且反复的过程,并且生成的数据与任何其他事件均不相关。 如果我们依靠此输出在屏幕上的事件顺序,例如verbose:gcverbose:classloading ,我们可能会取得一些进展,但是有一个更好的解决方案:Tuning Fork trace。

音叉轨迹

音叉可视化平台(请参阅参考资料 )最初是为了帮助开发和调试Metronome垃圾收集器而开发的。 (它也是一个可扩展的Eclipse插件,并且在IBM Toolkit for Data Collection和Visual Analysis for Multi-Core Systems中找到了更广泛的应用;请参阅参考资料 )。

使用音叉的优势包括:

  • 强大的可视化和分析功能
  • JVM活动数据的可用性:来自GC,类加载,JIT编译和其他组件
  • 将应用程序跟踪点与JVM跟踪数据相结合

添加的音叉应用程序跟踪点所需的代码更改在提供的源代码中进行了标记(Reader.java.instrumented和Writer.java.instrumented;请参见下载 )。

该代码比前面将计时数据写入标准错误输出流的示例要复杂一些,但是我们将向您展示这项额外工作的可观收益。 音叉跟踪点有两种:记录简单时间戳事件的跟踪点和代表程序员记录数据的跟踪点。 使用与内部JVM跟踪点相同的组件记录这两个事件,以进行类装入,JIT和GC活动。 至关重要的是,这确保了应用程序和JVM跟踪数据都使用相同的时间戳引擎-这意味着我们可以使用时间戳安全地关联所有事件,以查看在任何时间运行的事件。 尝试交错来自不同来源的跟踪数据通常充满了困难和错误。 我们唯一需要的音叉跟踪事件是时间戳记事件,我们将在感兴趣的应用程序代码区域的开始和结尾处添加时间戳记事件。

源代码中的其他代码用定界符标记,如以下示例所示:

/*---------------------TF INSTRUMENTATION START ------------------------- */writerTimer.start();
/* ---------------------TF INSTRUMENTATION END ------------------------- */

插入的源为Reader JVM(Reader.trace)生成一个跟踪文件,为Writer JVM(Writer.trace)生成一个跟踪文件。 这些二进制文件包含开始和停止事件,用于处理所有温度读取消息,以供以后使用Tuning Fork可视化器进行分析。

在音叉乐器版本中添加的代码位于以下区域:

  • 在音叉跟踪生成文件tuningForkTraceGeneration.jar中导入方法的语句
  • 记录器的初始化代码,用于编写并创建计时器和Feedlet (计时器和记录器之间的数据馈送)
  • 运行仪器初始化
  • 将feedlet绑定到当前线程的方法

唯一需要的其他代码是执行计时:

/*---------------------TF INSTRUMENTATION START ------------------------- */writerTimer.start();
/* ---------------------TF INSTRUMENTATION END ------------------------- */AbsoluteTime startTime = clock.getTime();Code to be timedRelativeTime timeTaken = stopTime.subtract(startTime);/* ---------------------TF INSTRUMENTATION START ------------------------- */writerTimer.stop();
/* ---------------------TF INSTRUMENTATION END ------------------------- */

在我们的示例中,“音叉”时序代码包装了标准演示程序的现有时序代码,因此我们从这两个时序中获得了很好的匹配:

AbsoluteTime startTime = clock.getTime();Code to be timedRelativeTime timeTaken = stopTime.subtract(startTime);

要在添加了Tuning Fork跟踪点的情况下构建和运行代码,我们只需要确保将TuningForkTraceGeneration.jar添加到类路径即可。

调整Fork JVM跟踪数据

安装用于WebSphere Real Time的Eclipse插件

从SourceForge下载音叉(请参阅参考资料 )并将其安装在系统上之后:

  1. 启动可视化器。
  2. 选择帮助>软件升级>搜索要安装的新功能> WebSphere Real Time的Tuning Fork扩展>新远程站点
  3. 输入名称和URL http://awwebx04.alphaworks.ibm.com/
    ettktechnologies/updates
    http://awwebx04.alphaworks.ibm.com/
    ettktechnologies/updates
    http://awwebx04.alphaworks.ibm.com/
    ettktechnologies/updates
  4. 当搜索结果返回要安装的功能列表时,请展开WebSphere Real Time框,然后选择TuningFork Real Time JVM Feature 2.0.0

要启用内部JVM数据的日志,我们将-XXgc:perfTraceLog= filename .trace标志添加到命令行。

音叉可视化工具是可以在Windows®或Linux上运行的Eclipse插件。 要为IBM WebSphere Real Time JVM启用现成的图形,我们需要添加一个单独的插件 。 (“音叉”基础结构是通用的,也可以用于其他Java,C和C ++应用程序)。

“音叉”中用于理解程序执行的最有用的视图只是一张随时间顺序显示的事件图片。 许多预定义的图形可用于WebSphere Real Time(请参阅参考资料 )。 这些视图提供了有用的JVM数据组合视图(例如,GC性能摘要)。

我们在演示应用程序中添加了一些简单的Tuning Fork计时工具。 此代码仅定义一个计时器,并在现有计时代码之前和之后立即启动和停止该计时器,该计时器将检查线程是否溢出:Reader为2毫秒,Writer为3毫秒。 图2中显示了此数据的一小部分的Tuning Fork视图,确认代码按预期运行:

图2.音叉跟踪—演示应用程序代码

图2显示读取器在大约130微秒内执行,并且套接字上发送的数据触发Writer线程运行大约900微秒(这是可以预期的,因为该线程还有很多工作要做)。 从温度读取到写入再到文件的整个数据传输仅需1毫秒多的时间,完全在5毫秒的限制内。 我们还可以看到Reader线程定期在其2毫秒的周期内醒来。

“音叉”可视化器会自动对齐两个数据源中的时间戳,因此“时间X”轴适用于两个线程。

在GC周期中,此模式会发生什么? 没有Tuning Fork,我们只能看到应用程序的观点,但是现在我们可以看到GC暂停如何以更清晰的方式影响我们的应用程序。 图3显示了Writer JVM中的GC活动视图:

图3.音叉跟踪— GC切片

在这里可以看到GC对Writer线程持续时间的影响。 节拍器完成的每个增量工作(一个量子(或音叉中的一个片段 ))运行大约500微秒。 在量子期间,Writer JVM中的所有应用程序线程都会暂停,因此,如果在Writer线程运行时发生了量子,则执行时间(通常)从900微秒增加到1.4毫秒。

请注意,对Writer线程的总扰动将比量子的500微秒略多; 肯定会发生上下文切换开销和潜在的处理器缓存污染影响。 如果Writer线程与GC量子线程之前的运行线程被分派到了另一个内核上,那么它将在特定于内核的缓存中产生更高的成本。

Reader线程在单独的JVM中运行,并且不受Writer运行时中GC活动的影响,因为计算机具有四个同时运行Writer JVM的GC线程和Reader的CPU内核。 (缺省情况下,WebSphere Real Time每个JVM使用一个GC线程。)

通过检查Writer JVM中的其他GC周期,我们可以看到一些异常时间超过了2毫秒。 在图4中更仔细地查看这些内容,很明显,它们很不幸地被两个GC定量干扰:

图4.音叉跟踪—两个GC切片

这些双重打击很少见,但是我们能完全避免吗?

为了不被两个量子碰撞,应用程序代码的持续时间加上一个量子需要适合两个GC量子之间的间隔,通常大约为一毫秒。 因此,Writer需要将其执行时间从大约900微秒减少到少于500微秒。 但是,由于以下几个原因,即使采用这种策略也不能总是保证避免与GC量子体发生冲突:

  • 安排GC暂停的时间略有差异,这是因为管理确保维持70%的mutator(应用程序线程)利用率的合同的方式。
  • 每个处理器内核通常都绑定有高优先级的内核线程来处理,例如中断和计时器。 尽管它们只运行很短的时间间隔,但它们的优先级可能高于应用程序或JVM线程,并且会干扰这些运行的时间。
  • JVM具有一个比用户线程或其他GC线程具有更高优先级的线程-GC警报线程,该线程每450微秒运行几微秒,以管理GC时间片。 如果操作系统调度程序将其与我们的应用程序或GC线程分配在同一内核上,则将发生较小的延迟。

在微秒级别检查应用程序执行开始揭示(有时很少)线程,内核和调度之间的交互。 有时可能有必要通过合并来自操作系统的数据来更全面地了解这些内容。 音叉也可以从Linux System Tap工具导入数据,但是我们将不在本文中介绍。 用于多核系统的IBM Toolkit for Data Collection和Visual Analysis也可以可视化此数据(请参阅参考资料 )。

更多作家离群值

如果运行了演示应用程序,则在启动Reader JVM之后,您可能会立即从Writer控制台中看到一堆消息。 我们将从以下消息开始:

Writer deadline missed after 0 good writes. Deadline was (3 ms, 0 ns), time taken was (48
ms, 858000 ns)

该消息报告说Writer第一次运行时,花了将近49毫秒来写一条记录,这比以后运行时(不到1毫秒)要长得多。 我们知道延迟的原因与将方法的字节码转换为本地代码的JIT活动无关,因为我们运行的是AOT编译的代码,并且在运行时禁用了JIT。 要考虑的另一个可疑区域是类加载,因为此问题在第一次调用时发生。 我们可以用音叉确认吗? 图5显示了Writer的第一次运行的音叉图:

图5.音叉跟踪—首先运行Writer,并进行类加载

正如我们所怀疑的那样,在Writer第一次运行之前和期间,我们看到了大量的类加载活动,明确确认类加载(和运行中的类初始化)是速度慢的原因。 随后的Writer运行在1毫秒或更短的范围内。

本系列的第2部分讨论了避免这些延迟的技术。 当我们使用较高的时间放大系数时,将显示Tuning Fork视图可以帮助我们识别要预加载的类的其他方法之一。 在图6中,我们看到org / apache / xerces / util / XMLChar花费了3毫秒以上的时间来加载:

图6.音叉跟踪—确定缓慢的类加载

尽管我们的应用程序非常简单,但是使用XML处理需要加载更多的类,因此在应用程序开始其对时间要求严格的阶段之前预先加载它们或使用虚拟初始运行非常重要。

端到端离群值

到目前为止,我们仅在Writer JVM中研究了离群值,但是我们的要求是在5毫秒内完成端到端处理。 我们没有看到来自Reader JVM的报告,说没有错过最后期限,甚至它的第一次迭代都在不到1毫秒的时间内运行,然后降低到大约140微秒。 添加Tuning Fork工具也会为我们提供图7中事件的统计信息。(3毫秒的异常发生在JVM被Control-C终止时,发生了与异常处理相关的类的后期加载。 第二部分讨论了这些罕见的路径和技术来识别和预加载所需的类-简单的预热是不够的。)

图7.音叉跟踪—阅读器统计

问题是,在读取器JVM的开始时,写入器JVM报告了从读取读取器JVM中的温度计到在写入器JVM上写入数据的完全传输数据的大量截止日期。

Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(122 ms, 93000 ns)
Writer deadline missed after 0 good writes. Deadline was (3 ms, 0 ns), time taken was
(48 ms, 858000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(122 ms, 517000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(121 ms, 567000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(120 ms, 541000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(119 ms, 525000 ns)

这种模式一直在逐渐减少超限,直到:

Data deadline missed after 0 good transfers. Deadline was
(5 ms, 0 ns), transit time was (10 ms, 585000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(9 ms, 588000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(8 ms, 531000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(7 ms, 469000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(6 ms, 398000 ns)
Data deadline missed after 0 good transfers. Deadline was (5 ms, 0 ns), transit time was
(5 ms, 518000 ns)
Writer deadline missed after 3087 good writes. Deadline was (3 ms, 0 ns), time taken was
(3 ms, 316000 ns)

因此,我们有一个最初的122毫秒的大延迟,该延迟稳定地减少,最终达到仅错过偶尔的Writer或Reader截止日期的状态。 图8显示了启动时的数据传输时间图:

图8.数据传输启动

除了第一个48毫秒的Writer持续时间外,在最初的约120个缓慢的数据传输过程中,没有读取器或Writer溢出的报告,那么延迟在哪里? 再次,我们的Tuning Fork跟踪可以提供帮助,这一次将来自JVM和两个应用程序线程的数据组合在一起,如图9所示。我们可以通过在命令中添加-verbose:gc来表明在任一JVM中尚未启动GC活动。行,但是类加载又可以负责吗?

图9. Tuning Fork跟踪— JVM和应用程序线程

在图9中,Reader JVM中的类加载是第三行。 不出所料,它已经完成了Reader的第一次运行。 最下面的行是Writer中的类加载。 通过将鼠标指针悬停在X轴上20到70毫秒之间的区域中的条形上,我们看到这些条形几乎都与XML有关。 再次,类加载延迟在我们的数据传输时间中占了很大一部分。 我们最长的延迟(122毫秒)是Reader计时器的第一个红色条与Writer的第一个绿色条的结尾之间的间隔(标记为49.88 ms)。 第二次传输的速度稍快一些,因此随着Writer在进入请求的待办事项中运行平缓的趋势持续,直到清除了待办事项并且数据传输在5毫秒内。 这解释了启动时错过数据传输截止日期的模式。 但是,类加载是唯一因素吗? 读写器JVM之间通过套接字进行的数据传输是否有帮助?

套接字延迟

使用套接字连接JVM(允许将应用程序拆分到两台计算机之间)可能会导致延迟。 我们对Reader和Writer应用程序代码进行了以下更改,以查看哪个起作用:

  • 禁用Nagle :Nagle算法(请参阅参考资料 )在实时系统中引起延迟是众所周知的,因为它在发送网络数据包之前先对它们进行缓冲。 Java应用程序可以使用socket.getTcpNoDelay()来测试Nagle设置,如果找到,则可以通过设置setTcpNoDelay(true)禁用Nagle设置。 但是,禁用Nagle不会影响缓慢的启动传输。
  • 使用PerformancePreferences :通过使用setPerformancePreferences(1, 2, 0) ,可以将默认套接字修改为更符合我们的要求。 这对低延迟最重要,其次是连接时间短,而对高带宽则最不重要。 将其添加到演示代码中可大大减少启动延迟—请参见图10:
    图10.音叉跟踪—使用套接字PerformancePreferences

    现在,由于类加载而导致的主要延迟减少到了约40毫秒,可以通过预加载这些类来消除这种延迟。

结论

本文总结了“ 用实时Java开发”系列。 正如整个系列所强调的那样,可预测性是实时应用程序的重中之重。 在本文中,我们设计并编写了一个简单的实时Java应用程序,并展示了如何使用计时器和工具的组合来分析其执行并验证其执行的可预测性。 在此过程中,我们解释了GC暂停和正在运行的应用程序之间的相互作用,展示了类加载如何导致延迟,并观察了在简单应用程序中使用XML的影响。 并且我们确定了已连接系统的端到端分析的重要性,并检测并减少了由网络套接字引起的延迟。

我们在本文中报告的性能数据是在受控环境中确定的。 在其他操作环境中获得的结果可能会有很大差异,因此您应验证适用于特定环境的数据。 还请注意,物理分布式应用程序受多种可变性因素的影响(请参阅参考资料 )。 我们通过在单台计算机上运行演示应用程序来减少或避免了其中的一些。 You can treat some of the Java network-tuning parameters that we've presented as hints.


翻译自: https://www.ibm.com/developerworks/java/library/j-rtjdev3/index.html

用java编写验证码程序_编写,验证和分析实时Java应用程序相关推荐

  1. Java FX8_第一篇_编写第一个Java FX Application

    所有的Java FX程序必须继承自Application类且必须覆写start( )方法,而start ( )方法的参数是一个stage(继承Application类且使用Stage类必须导入java ...

  2. 编写区块链_编写由区块链驱动的在线社区的综合指南

    编写区块链 by Sandeep Panda 通过Sandeep Panda 编写由区块链驱动的在线社区的综合指南 (A comprehensive guide to coding a blockch ...

  3. mac上java文件如何编译_如何在Mac上用Java编译和运行程序?

    小编典典 在Mac OSX或任何主要操作系统上编译和运行Java应用程序非常容易.Apple随OSX一起提供了一个功能齐全的Java运行时和开发环境,因此您要做的就是编写Java程序并使用内置工具来编 ...

  4. java程序设计清华大学出版社_清华大学出版社-图书详情-《Java程序设计基础》...

    出 版 说 明 我国高等学校计算机教育近年来迅猛发展,应用所学计算机知识解决实际问题,已经成为当代大学生的必备能力. 时代的进步与社会的发展对高等学校计算机教育的质量提出了更高.更新的要求.现在,很多 ...

  5. java o 验证码识别_验证码识别(一)

    开始做简单的ORC,从昨天到今天总算有个小小的成绩了. 图像的文字识别我拿验证码开刀,因为验证码稍微简单点,说说验证思路: 一.获取验证图片 二.程序加载要验证的字体库 三.程序加载需匹配的文字库(字 ...

  6. 精通java的拼写段子_[慎入]已经笑晕!只有程序员才懂的10个段子-Go语言中文社区...

    (1)有两个程序员:一个技术很牛.BUG很少,另一个技术很菜,BUG很多.那个很菜的程序员,经常被测试妹子叫去,接受批评教育. 后来,很菜的程序员跟测试的妹子擦出了火花,走到了一起. 技术很牛的程序员 ...

  7. java.policy无法修改_如何配置Policy文件进行Java安全策略的设置

    中国人最喜欢访问的网站 只要注册ofo就送你10块钱,还等什么,快来注册吧 Java语言具有完善的安全框架,从编程语言.编译器.解释程序到Java虚拟机,都能确保Java系统不被恶意的代码或敌对的编译 ...

  8. 构建meteor应用程序_我构建了一个渐进式Web应用程序并将其发布在3个应用程序商店中。 这是我学到的。...

    构建meteor应用程序 by JudahGabriel Himango 犹大(Gabriel Himango) 我构建了一个渐进式Web应用程序并将其发布在3个应用程序商店中. 这是我学到的. (I ...

  9. java moment 日期转换_关于日期:如何使用Java 8 DateTime API转换修改后的儒略日数字...

    我有一个数据库,用于存储日期和日期时间(分别为INTEGER和DOUBLE)作为已修改的儒略日数(MJD).修改后的儒略日数是自1858年11月17日午夜UTC以来连续的天数.根据定义,它们始终以UT ...

最新文章

  1. 大咖云集!航天智慧物流创意组-技术培训第三期
  2. OpenCASCADE:教程概述
  3. 修改thymeleaf默认路径
  4. HDU - 4280 Island Transport(最大流)
  5. 腾讯云短信服务使用记录与.NET Core C#代码分享
  6. 超详细前端开发案例:品优购商场项目(二)
  7. 关于未来世界,永生的几个方向和总结思考
  8. hdu 1507(最大匹配)
  9. Linux技术进阶示意图
  10. redis主从和持久化
  11. 苹果M1芯片兼容mysql吗_苹果m1芯片相当于什么水平
  12. java开源规则引擎比较_几款常用规则引擎的简单对比及演示
  13. Android组件化开发实践
  14. php loadclass,Laravel如何实现自动加载类
  15. algorithm头文件中的函数:remove()与remove_if() 函数,……
  16. Atitit 并发锁机制 艾提拉总结 目录 1. 2. 用的比较频繁锁 2 1.1. 语法锁sync api锁 2 1.2. 数据库标志位锁 2 1.3. 文件锁 2 2. 锁得类型 3 2 2.
  17. c 计算机操作步进器,雷赛_数字步进电机与驱动器_DM2C-RS556型号
  18. 哈理工OJ 1147 重生(水DP)
  19. Structured Streaming系列-5、物联网设备数据分析
  20. 汽车门把手的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告

热门文章

  1. 技术服务于生活——羽毛球+程序员=?
  2. 第三章、存款业务的核算
  3. HTML表头固定[1]
  4. windows中dllmain,创建进程和转发dll
  5. C#服务端的微信小游戏——多人在线角色扮演(十一)
  6. 花一个小时,打造自己的LoRaWAN网关
  7. Java_io流详解(代码)
  8. CAD中的dxf文件解析(二):dxflib的使用
  9. MTK常用函数快速查找
  10. p51 thinkpad 拆解_开箱晒物 篇三:美版2018款移动图形工作站ThinkPad P52开箱(拆键盘和后盖)...