扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
有些人可能会认为JAVA和实时是不同环境中的两个概念,实际上,最老的JSR之一(确切的说是第一个JSR)就是关于扩展JAVA平台的实时特性的。然而,任务提交的顺序并不保证它的实现的顺序;Sun只是在最近才实现了实时性,但这并不意味着它是一个低优先级的特性;实际上,这是一个非常复杂并且是一个完整的工作。但是实时的要求与JAVA的本身的要求兼容吗?有很多问题就不得不提了,如GC的语义学 ,同步,线程调度以及high-resolution的时间管理。在本文中,我们将逐一解释这些名词。
实时是什么意思呢?
Greg Bollella ,是Sun公司的一个杰出的工程师,实时JAVA规范的作者之一,它说,实时意味着“能够可靠的可预测的推测和控制程序逻辑的时间行为的能力。”实时并不像许多开发者想的那样,意味着速度,而是意味着当需要对现实世界的事件作出反应时,它的行为是可预测的和可靠的。实时的电脑总是在限定的期限之内作出反应。取决与所设定的的期限,大量的系统可以被称作是实时的。
很多程序不能允许即使是一秒的延迟;他们包括之前提到的金融软件,飞机控制软件,核电站控制软件等等。所以,这些并不完全是对速度要求很高的,尽管实时平台的设计师会努力使得程序变快。显而易见,标准的JAVA平台并不符合这些实时系统的要求,这也写入了J2SE和J2EE的许可证协议中,这些协议明确的声明JAVA不能用于核电站设施软件和防卫系统等等。
实时JAVA
开发实时应用程序需要一个能够允许开发者正确的控制程序的运行时间以及程序在现实中的行为PI集合和语义。因此JAVA的实时版本必须提供一些JVM的增强以及一个适合实时程序的API集合。毫不奇怪,在JAVA中添加实时的特性最大的障碍在于它的垃圾收集器。Sun最近发布的JAVA实时版本RTS1.0中就包含了一个革命性的核心的实时的垃圾收集器。尽管它的第一个实现中并没有包含这样一个垃圾收集器(将在下一个release版本中将增加)。JAVA RTS 提出了其他一些问题,保证线程调度的确定性,overhead同步,锁排队管理,类初始化以及最少的中断反应延迟。JAVA RTS仅仅针对于合适的操作系统,这就意味着只有诸如QNX这样的的实时操作系统才适合去实现一个这样的JVM。
实时JAVA规范的第一个官方商业实现版本是在Solaris 10,工作在UltraSparc硬件上,并且要求J2SE 1.4.2作为基础。未来的版本将会支持JAVA 5 以及其他的一些平台。美国海军,Raytheon公司和波音公司已经开始使用SUN的JAVA实时系统。当然,SUN的JAVA实时系统并不是第一个实时JAVA的实现。一些嵌入式系统的厂商已经在他们的系统中实现了一些实时的特性,不过他们的实现只是涵盖了一些具体的需要,并不符合JSR-1规范的要求。这对于那些使用JAVA平台并需要实时JVM的开发人员来说是个好消息。
这些听起来都不错,可是从一个开发人员的角度来看,这又意味着什么呢?要改变现有的程序使其使用RTS的API需要些什么改变呢?我们可以摆脱垃圾收集导致的中断这样一个主要的问题吗?很不幸,所有的一切并不是那么简单。仅仅简单的安装一个RTS的扩展包,把java.lang.Thread实例改名交javax.realtime.RealtimeThread并不能把一个程序变成一个实时的应用程序。
不过,这仍然是一个很好的开端,至少你可以获得一个革命性的实时的垃圾收集器。不得不提的是现有的J2SE的程序将可以成功的在JAVA 实时系统下运行 因为RTSJ规范只是JAVA语言规范和JAVA虚拟机规范的一个子集。它并不允许那些可能会破坏现有程序的语义扩展。
为了使得实时的垃圾收集器可预测,程序员必须了解它的程序是如何从堆中要求内存的,因为垃圾收集器和程序都要用到它。程序产生垃圾,然后垃圾收集器将垃圾清理成空闲的内存,它们需要在堆中进行。因此,你必须告诉垃圾收集器关于你的程序产生垃圾的速度等一些信息,这样它可以明白自己需要多快的进行垃圾收集。如何获取那些数字可能是有点tricky,但是不管你做什么,你必须得考虑内存的使用。
如果运行RealtimeThread不是足够的,在修改完大骂之后,垃圾收集器的停顿将仍然很长或者无法预测。你可能需要使用一个execution context而不是RealtimeThread,例如javax.realtime.NoHeapRealtimeThread.它可以通过使用内存而不是JAVA堆来获得可预测的特性,例如immortal memory 和 scoped memory,后面我们将讨论他们。获得可预测性当然需要代价的:典型的情况是牺牲了系统的平均性能。
JAVA RTS的新特性
让我们来看一下JAVA RTS平台中增加了哪些新特性。
*直接内存存取.JAVA RTS 允许对物理内存的直接存取,这与J2ME很像。不要惊奇,JAVA实时系统主要针对的平台就是嵌入式系统。这就意味着现在你可以创建用纯JAVA写的设备驱动了。尽管内存存取并不是一个实时系统的直接要求,许多应用程序还是需要对物理内存做存取。JAVA RST定义了一个新的类,这个类允许程序员对物理内存做字节级别的存取,同时这个类还允许在物理内存中创建对象。有人可能会认为JAVA支持物理内存存取就是放弃了原有的主要的原则-可靠性和安全性,并向C语言又靠近了一步。但这并不是问题的所在,JAVA通过控制内存边界和数据内容来实现了一个强大的安全保护措施。
*异步交流。JAVA RTS 提供了两种异步交流的形式:异步事件处理和异步传输控制。异步事件处理意味这开发者可以计划对来自JVM外部的事件的反应。异步传输控制为一个线程提供了安全的中断另一个线程的方法。
*High-resolution timing.有很多详细描述High-resolution timing的方法,包括绝对时间和相对时间。时间的调度和度量能够具有一个纳秒级准确度。
*内存管理。有两种新的内存区域可以帮助防止由于在实时应用程序中传统的垃圾回收导致的无法预期的延迟。Immortal memory 和 scoped memory。Immortal memory保存对象而不摧毁他们,直到程序结束。这就意味着在Immortal memory中创建的对象必须像C 程序那样仔细的分配和管理。scoped memory仅仅被用于当一个进程在一个特定的范围里工作的情形。当这个进程离开这个范围的时候,对象将自动被摧毁。Immortal memory和scoped memory都不会被垃圾收集的,因此可以通过使用它们来避免垃圾收集的影响。JAVA RTS也为使用内存区域的线程提供了内存分配预算的功能的有限支持。当线程被创建的时候,每个实时线程的最大内存区域消费和最大的分配率可以是指定的。
*实时线程。正如先前所提到的,JAVA RT支持两种新的线程模型:实时线程(javax.realtime.RealtimeThread) 和非堆实时线程(javax.realtime.NoHeapRealtimeThread).这两种线程类型都是不能被垃圾收集中断的。这些线程具有28个级别的优先级,并且和标准的JAVA不同,他们的优先级是严格的增强的。实时线程是同步的,并且并不受限于所谓的优先级颠倒(priority inversion),在这种priority inversion情况下,如果一个低优先级的线程拥有一个高优先级的线程所需要的资源,将会阻止了这个高优先级的线程的运行。测试证明JAVA RTS完全避免了priority inversion,这对于紧急任务来说是很重要的。
它是如何工作的
让我们先简短的了解程序员可以多么轻易的利用这些实时JAVA的新特性的。我们将只考虑新的API中的最有趣的部分:线程和内存。对于其他问题,请参阅real-time JAVA specification 文档(PDF).
规范的制订者们一个主要的目标就是保证RTS编程的简单性,尽管实时问题是很复杂的;这就意味着操作系统必须尽可能多的完成一些工作,使得程序员只需要完成实时程序设计这个具有挑战性的工作。
线程
RealtimeThread 类继承了java.lang.Thread类。这个类有多个构造函数,开发者能够调试线程的行为。
public RealtimeThread() public RealtimeThread(SchedulingParameters scheduling) public RealtimeThread(SchedulingParameters scheduling, ReleaseParameters release) |
提供给RealtimeThread(还有MemoryParameters )的构造函数的两个参数ReleaseParameters 和SchedulingParameters使得线程的时间和处理器需求可以通知给系统。RealtimeThread实现了Schedulable接口。关键就在于Schedulable对象可以被放置在ImmortalMemory, HeapMemory, ScopedPhysicalMemory, and PhysicalImmortal等类的实例所表示的内存中。
NoHeapRealtimeThread 是RealtimeThread的一个特有的形式。因为NoHeapRealtimeThread实例可以立刻抢占任何已实现的垃圾收集器,它的run()函数中的逻辑不允许分配或引用任何在堆中分配的对象,或者是对在堆中的对象进行操作。例如,如果A和B是immortal memory中的两个对象。B.p是堆中的一个对象的引用 , A.p和B.p的类型是一致的,那么NoHeapRealtimeThread是不允许执行与下面类似的代码的:
A.p = B.p; B.p = null; |
考虑到这些限制,NoHeapRealtimeThread对象必须被放置在一个内存区域中以防止线程可能unexceptionally的控制实例的变量。这就是为什么NoHeapRealtimeThread的构造函数要求ScopedMemory 或 ImmortalMemory的引用了。当线程启动后,所有的操作都处于分配的内存区域中。因此,new操作产生的所有的内存分配都处于这个区域中。
内存管理
我们已经发现了一些内存相关的类了。更确切的说,MemoryArea 是所有处理可分配的内存区域的类的抽象的基类,这些内存区域包括ImmortalMemory,物理内存和ScopedMemory。HeapMemory类是一个单体(singleton)对象,它允许其他内存区域中的代码在JAVA堆中分配内存。这个方法返回一个对HeapMemory单体实例所代表的JAVA堆的指针。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者