WEB开发网
开发学院软件开发Java Google Dart精粹:应用构建,快照和隔离体 阅读

Google Dart精粹:应用构建,快照和隔离体

 2012-03-16 15:38:28 来源:WEB开发网   
核心提示:既然程序设计语言已经有了上千种,为什么Google 还要推出Google Dart呢?它又会增加什么样的特性呢?答案很简单:Google Dart团队想要开发一门适合现代应用程序开发的语言,Google Dart精粹:应用构建,快照和隔离体,它既能在服务端工作,也能在(移动)客户端工作,无论是决定实例化某个类,还是分配

既然程序设计语言已经有了上千种,为什么Google 还要推出Google Dart呢?它又会增加什么样的特性呢?答案很简单:Google Dart团队想要开发一门适合现代应用程序开发的语言,它既能在服务端工作,也能在(移动)客户端工作。

Dart的一些特性解决了像Java或Javascript语言长久以来存在的问题。它的快照功能类似于Smalltalk的映像(image),使用快照不仅可以带来(接近)即时的应用程序启动速度,而且还没有映像遗留的一些问题。隔离体特性可以确保代码在无共享状态的单线程内执行,它的消息传递并发类似于Javascript中的WebWorker和Erlang中的进程。Dart的这些语言特性使得我们可以开发可扩展的和模块化的应用。Dart代码既可以被DartC编译器编译成普通的Javascript,也可以在Dart虚拟机(Dart VM)中执行。

下面InfoQ将介绍应用程序开发中使用Dart的乐趣所在,并重点介绍Dart虚拟机以及一些值得关注的语言特性。

Dart是一门应用程序语言:快照和初始化

应用程序的启动时间真得有那么重要吗?用户在每一天又会重启集成开发环境(IDE)或者字处理器软件多少次呢?随着内存有限的移动设备的兴起,应用程序启动变得非常频繁;移动设备操作系统中的内存不足(OOM)杀手进程显得战意十足,因为它会毫不犹豫地杀死处于中止状态的应用程序。iOS中的多任务模型及其赫赫有名的物理Home按钮,也同样在缩短移动应用程序的平均寿命。在iOS4之前,按下 Home按钮总会杀死正在运行的应用程序;而在iOS4中,虽然情况变得稍显复杂,但是应用程序仍然必须准备随时“死”去,“死”在用户手里或是OOM进程下。

然而这样的行为在移动操作系统上并不会长久。“突然终止”和“自动终止”是OS X最近版本中才推出的应用程序属性,它们被用来声明该应用程序可以处理在任何时候被杀死(例如,当可用内存非常低的时候)并重新启动,且整个过程对用户透明。

启动缓慢从Java 1.0开始就是Java GUI应用程序的头疼问题。启动一个大型的Java应用程序需要大量的工作:需要读取、解析、加载和链接数以千计的类;在Java 1.6之前,这个过程包含了为方法生成堆栈图以验证字节码。然而一旦类被加载,仍然需要初始化(静态初始化等)。

对于当下的Java GUI应用程序来说,仅仅显示初始GUI就需要大量的工作。虽然这个问题一直影响着开发人员和用户,但是Java 6中引入的闪屏API(SplashScreen API)标志着该问题尚未得到解决。

快照 vs Smalltalk映像

为了解决启动缓慢的问题,Dart使用了堆快照功能,它类似于Smalltalk的映像系统。所谓堆快照,其实就是遍历应用程序堆并将所有的对象写入文件。目前Dart的发布版本上有一个工具可以帮助激活Dart虚拟机、加载应用程序代码并在调用main之前采集一份堆快照。随后Dart虚拟机可以使用这个快照文件快速地加载应用程序。

快照还可以用于对Dart虚拟机中的隔离体之间发送的对象图进行序列化。

在Dart的最初技术预览版本中,似乎并没有什么API可以用作初始化快照,尽管这很有必要。

快照的技术细节

Dart团队在快照格式上投入了很多努力。首先,快照需要能够移动到不同的机器上工作,不管目标机器是32位,64位还是其他。同时,快照还需要能够被快速地被读进内存并尽量减少的额外工作,如指针修正。

更多详细信息,请参考runtime/vm/snapshot.cc和runtime/vm/snapshot_test.cc 以了解快照系统的使用方法:导出完整快照,读取快照,从快照启动隔离体等等。

快照 vs Smalltalk映像

Smalltalk映像特性并没有普及;Gilad Bracha曾写过一篇文章讨论实践中Smalltalk映像的问题。 Smalltalk开发通常发生在某个映像之中,其中的无用代码会被剥离且映像会被冻结以进行部署。Dart的快照与之不同,因为它们不仅是可选的,而且需要通过启动应用程序并采集快照才可以生成。由于Dart缺少动态代码评估以及其他的代码加载特性,因此剥离过程变得更加彻底。

Dart的快照功能目前在DartC编译后的Javascript的代码中不受支持。

快照也被用作隔离体间的消息传递;消息发送前在一端为对象使用SnapshotWriter进行序列化而后从另一端进行反序列化并读取。

不管怎样,快照功能就在Dart虚拟机和工具中。同Dart的许多其他特性一样,快照该怎么用要由社区说的算。

最后我要说的是,Google的V8 Javascript引擎中也有了快照的功能,它通过从快照中加载Javascript标准库来改善启动速度。

初始化

即使没有快照,Dart也被设计成尽量避免在启动时进行初始化。Dart中的类是声明性的,也就是说创建它们并不需要执行什么代码。库可以定义final的顶级元素,即类之外的函数和变量,但需保证它们为编译期常量(查看语言规范中的10.1节)。

与Java中的静态构造器,或是那些在启动时依赖各种元编程方法生成数据结构、对象系统等语言相比,Dart最优化了应用程序的启动时间。

Dart目前并没有反射机制,但是看起来基于Mirrors (PDF)的实现会在不久的将来出现在语言中,通过它也许能够使用API构建代码并将其加载入新的隔离体中,从而将元编程带进Dart。

并发单元,安全和应用:隔离体

并发

隔离体是Dart中的基本并发单元。每个隔离体是单线程的。为了能够在后台执行工作或是利用多核或多处理器,需要启动新的隔离体。

Google V8最近同样增加了隔离体特性,隔离体的加入为V8的内置程序提供了方便,通过在同一个系统进程中启动多个隔离体可以实现成本更低的Web Worker;这个特性暂未对Javascript开放。

这种为并发而存在的多个独立隔离体模型类似于Javascript和Erlang。Node.js也需要使用进程来利用多处理器或多核;许多管理Node.js进程的解决方案已经横空出世。

其他的单线程或绿色线程(Green Thread)语言都有类似的进程管理方案。Ruby中的Phusion Passenger就是一个例子,它试图解决在多个进程内加载相同代码的开销问题:Phusion Passenger首先会加载Rails应用程序,并使用系统调用fork快速创建具有相同程序内容的多个进程,从而避免重复多次地解析和初始化相同的应用程序。Dart的快照特性用另一种方式解决了这个问题。

可靠性

Dart的首个技术预览版本为每个隔离体使用一个线程,但是与此同时其他模型也正在考虑之中,如将多个隔离体多路传输到一个线程中或将隔离体运行在不同的系统进程中,这些做法同样可以让隔离体运行在不同的机器上。

将应用程序分割成多个独立的进程(或隔离体)有助于提供可靠性:如果某个隔离体崩溃,其他隔离体将不受影响,同时隔离体的干净重启也成为可能。 Erlang的监控树(supervision trees)在这个模型中十分受用,体现在它能够监控进程群的存活和死亡状态并写入自定义策略用来处理进程死亡。

这篇Akka和Erjang创始人访谈 文章中对Erlang模型的优势给出了一个很好的概述。

安全性

不受信任的代码可以运行在自己的隔离体中。所有与之的通信必须通过消息传递发生,附加的能力-风格(capability-style)机制可以限制隔离体中谁可以与哪个端口进行会话。隔离体必须给定一个信息发送的端口,否则它什么也干不了。

内存划分

将应用程序分割成隔离体还有一个好处:每个隔离体的堆都是独立的;其中所有的对象都完全的属于它自身,这不同于共享内存环境下的对象。其实关键的好处在于:如果为某个任务启动了隔离体,那么在任务结束时整个隔离体可以一次性被回收而不需要运行垃圾回收器。

此外,如果一个应用程序被分割成多个隔离体,那么就意味着该应用程序使用的内存被分割成更小的堆,即小于应用程序使用的内存总量。每个堆由它自己的垃圾回收器所管辖,这带来的效果就是一个隔离体中的完整垃圾回收器只能工作在该隔离体之中而不会影响其他隔离体。 对那些易受垃圾回收器中断(GC pauses)影响的GUI应用程序和服务器应用程序,一则好消息是:那些让垃圾回收器一直忙个不停的糟糕隔离体将不会影响到易受时间影响的组件。因此,让每个隔离体拥有各自的堆可以提高模块化程度:每个隔离体控制自己的垃圾回收器中断行为,且不会受到其他组件的影响。

虽然Java和.NET中的垃圾回收器已经改善了许多,然而垃圾回收器中断对于GUI应用程序以及时间敏感的服务器应用程序仍然是一个重要的问题。虽然类似Azul垃圾回收器的解决方案已经可以使中断变得可控甚至几乎消失,但是它们要么需要特殊的硬件,要么需要访问系统基础设施中的底层,如基于x86架构的Zing,实时垃圾回收器确实存在,尽管如此,它们还是牺牲了执行速度来换取可预测的中断。

将内存分割成单独的堆意味着垃圾回收器的实现可以变得更加简单且依旧足够得快。当然,这些都取决于开发人员——想要受益于这些特性,一个应用程序就必须分割成多个隔离体。

依赖注入不再必要:Dart中的接口和工厂

常常听说应当“面向接口编程”,然而这么做在实践中显得有点困难,因为在调用时用户不得不在new调用后跟上实际的类名。在Java世界里,这个问题导致了依赖注入(Dependency Injection, DI)框架的产生。采用依赖注入框架,就意味着首先要在项目中注入特定的DI框架依赖。

那么依赖注入解决了什么问题?在特定类上调用new不仅需要硬编码类,还会给测试以及代码灵活性带来问题。毕竟,如果所有的代码都基于某个接口编写的话,那么具体怎么实现将没有关系,且用户应当为使用案例选择正确的实现方式。

Dart目前的版本中也有一种依赖注入的解决方案,使用它可以让许多选项需要选择的情况变得不再必要。Dart通过在语言之中将接口链接至代码来实例化对象。所有这一切需要的灵活性都隐藏在了工厂(Factory)之中,无论是决定实例化某个类,还是分配某个新的对象,抑或只是返回一个缓存对象。

1 2  下一页

Tags:Google Dart 精粹

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接