2007年08月16日

 Mark Lam has been a virtual machine engineer in the JavaME CDC team at Sun Microsystems for over 6 years. Before joining Sun, he was a real-time embedded systems developer for 6+ years, working on application frameworks, graphics systems, networking protocols, game development, and fault tolerant systems amongst other things, on devices ranging from 64KB 8bit uControllers to 32-bit RISC machines.

原文URLhttp://weblogs.java.net/blog/mlam/archive/2006/11/the_big_picture.html

 

         亲自的,当你接触一个新系统,第一件事是你想要搞清楚每一个东西是怎么结合在一起的。如果你像我一样是个虚拟思考者,最好的一条路就是画一个图表来说明你觉得重要的东西以及他们之间的联系。嵌入式系统的一个例子,以我的经验,知道内存里有什么在哪儿,系统资源是怎样使用的也同样重要,因此我画了一个数据结构的图表,下面:

CVM的世界参考物?

下面是怎么来读这张图

根数据结构

       CVM的一个设计标准是可重新启动,甚至当你在没有进程的OS上运行它。没有进程需求的可重启性,我们能够释放所有分配的内存。为了让生活更容易(它总是一个很好的实践),我们必须确保所有的数据从单一数据结构书的根上是可获得的。这个根数据结构是CVMglobals,你能在上面地图的左边看到。你会在globals.hglobals.c里找到CVMglobals的定义(也可以在这个文件里找到CVMGlobalState)。看下CVMglobals,你会发现它是一个系统全局数据结构的集合。保证全局在一个地方可以让它更容易恢复已知的初始值。例如:使用memsetting把所有的值设为0(在外面清理了所有的数据后)。

垃圾回收和Java堆栈:

       从全局,你可以找到一个内嵌的结构,它把握GC的配置和管理信息(CVMglobals.gc)。从这些,你最终可以得到Java heap。

       CVM有可插件的GC体系。插件是编译时的可插件性,不是运行时。它允许在CVM里根据实验来试验GC。当前,唯一一个有品质保证的CVM的GC是世代的(generational)GC(在这儿这儿可以看到GC的细节执行文件)。

       所有的java对象,也就是所有从java.lang.Object扩展的对象,都从java heap上分配。只有一个例外就是ROMized Java objects。它们存在于全局数据。javaheap自己从C heap上分配。所有其它数据结构都从全局数据(i.e. .bss, .data, or their equivalents)或从C heap分配。

JIT和编译后代码

       CVMglobals为JIT(CVMglobals.jit)掌握配置和管理记录。通过树,你最后会找到JIT代码缓冲。这个代码缓存当前是固定大小的(通过运行时可配置)它在VM启动时分配。一旦它被分配,它的大小不能被改变。

       当java方法被JIT编译,编译器生产的比特(通常作为编译后方法提及)会存在代码缓存中。编译后方法的原数据(meta-data)(由JIT生成)也会存储在代码缓存中。因此,代码缓存的大小会规定,间接的,有多少方法能被编译。

Java对象和类

       当类文件装载到内存,内容主要被解析和组织到一个最佳的结构,它从C heap里分配。这个结构叫CVMClassBlock,它掌握所有类的元数据。原数据包括常量池,类属性,域和方法信息,bytecode等等。对每一个CVMClassBlock,有一个从java heap分配的java.lang.Class的实例。一旦一个类已经被装载,它们总是作为一对存在。类块(classblock)有一个参考对一个类,反之亦然。当一个类unload时,它们会一起被释放。

       CVM里的每一个java对象有一个2个字的头。第一个字包含了一个指针指向类块。无论如何,这个头对java代码不可见,它只对VM里的C方面可见。注意:自从java.lang.Class扩展自java.lang.Object后,类实例也有2个字的头

       查找关键文件在objects.h and classes.h。 看

Java线程

       要执行任何东西,VM必须有线程。每个java线程由CVMExecEnv表现(通常缩写成ee)。在VM,本质上ee是 线程的标示符。所有线程操作请求作为当前执行的线程的ee的参数。看interpreter.h 和 interpreter.c 

       这儿有一个一对一的映射关系在ee和java.lang.Thread实例之间。一旦线程被初始化,它们2个就作为一对存在。

       这儿有一个一对一的映射关系在ee和JNIEnv之间。JNIEnv是一个嵌入的,作为ee的一个域。ee和JNIEnv之间的映射地址的基本需求只是偏移调整。

       所有ee被装载在一个链表里。这个链表的头是CVMglobals.threadList。主线程的ee被做为嵌入域在CVMglobals被分配。

系统互斥

       VM线程列表的操作需要被同步。在VM里的其他子系统和资源都一样。同步一般都由CVMSysMutex(看sync.hsync.c)。在VM启动时有几个系统互斥量(sysMutexes)被分配。这些互斥量只对VMC代码可见。只能被C代码使用。

       每个互斥量都有明确的目的(CVMglobals.threadLock用来同步线程),它是有序的。为了防止死锁,系统互斥量只能在增加序号时被锁住。当CVM使用断言建立时,升序可以被断言。

Java执行堆

       一个执行的线程必须有一个执行堆。在CVM里,每一个Java线程有2个物理堆:一个本地堆,一个java堆。本地堆由系统分配,用于C代码执行。它掌握本地代码的活动记录(堆帧 stack frames),VM代码包括解释器循环功能。它掌握所有JIT编译后代码的活动帧。

    Java堆(也叫解释器堆)用来保存所有java方法的活动记录。每个java方法一执行,一个帧会推入堆中。堆和帧数据结构定义在stacks.hstacks.c中。

    如果你在执行java方法时dump一个本地堆的跟踪,你会看到C代码的堆帧(stack frames)和解释器循环。如果你dump一个java堆,你只能看到java代码的堆帧当它们被调用时。如果你有一个本地方法在执行链(invocation chain),你可以看到本地堆和java堆的堆帧。这是因为本地方法是C功能和java方法的和。

GC根和根堆

GC阶段,CVM被称为一个精确的VM。这意味着在GC时间,我们能够确切的知道所有的对象指针在系统的哪个地方。这和需要你猜测某个内存片包含对象指针和一些类似对象指针的随即数据的老式GC系统有鲜明的对比。

      VM里所有能获得的对象能够通过跟踪被叫做垃圾回收根树的对象参考树来找到。这个树从一个根参考开始。这些根参考本质上是全局的。用来存储被称为根堆(root stacks)的数据结构。有一个例子是CVMglobals.globalRoots。准确的说,这些数据结构不需要成为堆。它们被作为链表来使用。无论如何,我们的java堆数据结构有道具来实现GC根堆的需要,不要请求我们写附加的代码(为了代码效率),因此我们只使用堆。

       如果一个对象不能通过跟踪根树来找到,那么对象是不可到的,它可以被GC再生。

       注意在穿越树中,任何的点,一个节点都可能是新的子树的根。因此,term root或者GC根有时被用来指向一个对象指针/参考,它总能通过根查找来找到。GC根能在根堆里找到,在线成执行堆,在对象和类域里。

终了

      给了你们上面的主意足够让你们了解CVM里最主要的数据结构。注意:我告诉你们的大部分事给了你们一个很好的概念模型。实践上,这儿有一系列的原因来导致异常。某些时候,这些异常会打破规则。另外一些时候,他们看起来象是扩展了规则。为了保持事情简单,我忽略了它们。当我在谈到每个子系统或特殊的数据结构的时候我会详细讨论它们。

       在上面,我忽略了一些有趣的细节,比如为什么从C heap 分配数据结构相对于java heap。几天或几周后,我会放大CVM的子系统或数据结构,详细的讨论它们。这包括机械的细节作为设计体系。

祝有美好的一天!!

2007年08月10日

Mark Lam has been a virtual machine engineer in the JavaME CDC team at Sun Microsystems for over 6 years. Before joining Sun, he was a real-time embedded systems developer for 6+ years, working on application frameworks, graphics systems, networking protocols, game development, and fault tolerant systems amongst other things, on devices ranging from 64KB 8bit uControllers to 32-bit RISC machines.

原文URLhttp://weblogs.java.net/blog/mlam/archive/2006/11/multitasking_th_1.html

 

         今天,我在java.net论坛看到了这个讨论。它让我感到惊奇,假如所有人都意味着同一个事情,如果当他们在谈论java平台的多任务时。因此,我决定今天暂停讨论CVM,开始多任务(这也与phoneME和CVM相关)

不承诺:在开始前,我必须阐明我的观点只代表我个人,不代表Sun,我的雇主,我在Sun的同事。

什么是多任务?

       严格的说,多任务意味着同一时间能做多个事情。对JAVA平台,这意味着能够有同时执行的代码。JAVA平台已经支持多任务。因此,这个问题是什么?人们想同时更好的运行多个程序。这到底和从不同的线程调用程序的main()函数有什么不同?不同是,程序想要自己的世界和所有的资源。简单的运行它们在不同的线程可能只有边缘效果,当一个程序能同其他操作交互的时候。因此,一个多任务JAVA平台需要能够分别隔离那些程序。那么,我在那里听到的这些特性点和行为?为什么这是现在操作系统的普遍应用。

       因此,当人们想要多任务的时候,我想他们是想要求一个JAVA进程,JAVA平台代替这些系统相关的角色。让我们看看多任务特点在系统中,看看这些在JAVA平台下证明了什么。我们应该关心人们想要的这些特点。让我们先从系统开始。。。

系统透视

       如果我们为JAVA平台处理,他们应该有下面的特点:

特点                            JAVA平台包含

同时(Concurrency)        多程序能同一时间运行在同一设备上。系统能够

                  保证程序间资源的合理分配

隔离(Isolation)          每个程序必须有自己的VM环境。一个程序不能同

                  其他同时运行的程序冲突。在错误的程序环境中,              

                  错误和不良行为能被检测

可靠的终止(Reliable Termination)  当程序出错,程序管理器必须能够中止程序并可靠

                  的回收资源

效率(Efficiency)          减少资源使用的冗余,在程序间最大限度的共享

                  资源。在JAVA VM对每个程序初始化的时候减

                  少冗余

JSR 121 Application Isolation API 定义了API来管理这些特点。同隔离成为系统处理。无论如何,有API并不是就有了执行。

人们想要什么

       这儿有一些解释关于人们为什么想要多任务。这些例子不一定能详细,但我想它们很现实,能够马上解决这个问题。

问题1:你工作在一个运行不支持进程简单嵌入式系统的设备,你不能提供系统升级因为所有的本地代码你都已经投资。升级意味着移植所有的原始代码到新的系统,这非常昂贵。你已经为所有的系统代码的许可付了钱。但是你想同时在设备上运行多个程序,程序由你不能控制的第三方提供。用户甚至需要运行一些从网络上下载的程序。不幸的,如果你的设备crash或者开始运行缓慢,你的用户将责备你而不是程序,尽管他10岁的儿子是从不安全的站点下载的程序。

       那么,你考虑。。。JAVA平台是一个虚拟运行环境,为什么你不仅仅让它保持,当程序要向其他程序或系统做坏事的时候?问题解决!

问题2你写了一个浏览程序,需要运行JAVA程序作为插件。当你不需要它时,你可以让它到一边去而不消耗资源。你想JAVA VM提供这些。但现在,你的系统本地API不能给你一个方便的方法让你做这个。自从JAVA VM被作为一个库被装载来匹配你的浏览程序。如果你使用系统特性来赶走VM,你将结束你的浏览程序,或者让它僵死而挂起。这会让你看到不好的东西。

       那么,你考虑。。。JAVA VM是一个装载程序的东西,对吗?为什么你不把程序结束并让它自己清理干净?问题解决。

问题3你需要同时在你的设备上运行多个java程序,但系统不让java程序共享资源。。。或者资源不够。

       你考虑。。。不少所有的java程序都需要同一个class libraries来运行?为什么JAVA VM不让他们共享内存中的同一副本?问题解决!

不够快

考虑这些。。。

       对问题1,为什么执行器不考虑执行隔离在它自己的程序里,或者改善嵌入系统来做这些?毕竟JAVA VM也是软件的一块,它也从它的程序里被启动。如果它能够改善它的系统隔离能力,那每一个启动的JAVA VM都能有它自己的进程,它的问题就会被解决。

       对问题2,为什么执行器不考虑执行,在浏览程序里,一个资源跟踪系统捕获所有被JAVA VM使用的资源和正在运行的程序?如果浏览器能跟踪所有它的资源,那么它能杀死VM和程序线程,收回所有它自己的资源。

       对问题3,为什么执行器不试着改善系统(或者询问系统提供商)来多更好的工作在共享普通资源?

       对所有3个问题的答案:这是很困难的。。。非常困难。转移这些问题到JAVA平台不会让它变容易。现在好了,这儿有几个信息,JAVA VM有可能很高机会完成这个目标。但本质上,这个问题没有变容易。

       我只想让你考虑这个问题的复杂性。当你自己解决了一个问题,你的感谢为它改变。多任务不是一个价值不高的特性,Sun或其他JAVA平台提供商能够发出其他弹点在他们的成品线路图。执行多任务是一个主要承诺

       Sun已经发出了2个多任务虚拟机解决方案对CLDCCDC,让我们看看它们怎么工作?

引擎盖下是什么?

       上面3个举例的每一个是多任务的3个不同的需要:隔离(isolation)、可靠的终止(reliable termination)、效率(efficiency)。在现实生活中,这些特性的几个或全部都需要集中在一起。

       我不会同时谈论这些特性,因为它们不是解决方案的难点。JAVA平台已经支持同时执行线程。它不是太多延伸对建立程序并发。除非你需要一些特殊的进程安排道具,我会离开这个讨论。

隔离(isolation

       CLDC不支持一个本地接口对本地方法。在实践中,大多数的CLDC程序是MIDlets,它是100%的纯java代码。因此,CLDC MVM只需要处理java statejava bugs的隔离

       java state方面,我的意思是类静态域(class static fields),一个实例被系统库使用。系统库被所有的程序使用。不需要隔离,每个程序都能够在系统库里看到其他程序的操作。

       一个java bug的例子是僵死状态。一个程序有2个线程同步操作2个不同的对象在对立面。结果,每个线程将会锁住来等待另外的线程来释放其它的锁定,每个线程将永远锁定来等待其它线程。无隔离,其它程序线程可能会同步同一个对象并永久的锁住。因此,一个行为不好的程序问题会导致其它行为良好的程序。

CLDC隔离

       CLDC MVM的解决方法是复制(replicating)所有静态域在系统库中。每个隔离得到它自己的一份拷贝。结果,类/静态初始化需要对每个隔离运行一次,来保证静态拷贝的初始化是隔离的。在这个机制下,每个隔离会创建它们自己单一对象的拷贝在系统中。

    CLDC隔离同样涉及一些小把戏来确保不同的程序得到不同的锁定实例当同步在ROMized 类和字符串实例这些不能在 JAVA VM中复制的。实际字符串(interned strings)被同样的对待。

    拥有java 状态自动隔离结果在java bugs的隔离中。如果一个程序僵死,其他程序不会受到影响因为僵死程序的java state对它们不可见。

    这儿有另外一种java bug:恶意程序有目的的做一些拒绝服务(DoS)攻击对其他程序。一个例子是消耗完所有可用内存让其他程序不能运行。CLDC MVM依靠每个隔离实行资源配给来解决这个问题。如果一个隔离试图获得额外的资源,它会得到一个内存不够(OutOfMemoryError)的错误,而其他程序仍可以分配内存 。

CDC隔离

       CDC需要为本地方法支持java本地接口(JNI)。因此,CDC VM需要为每个本地状态或错误提供隔离。本地状态,我的意思是本地代码的全局和静态变量。本地错误包括那些残留在内存中的废指针,分裂的块(segmentation faults),非法的指令等等。

    掌握这些是在系统进程中隔离本地代码的唯一出路。。。对每一个程序。CDC VM做这些是完全正确的。它使用特殊的处理类型叉子(fork)来生产新的隔离。每个隔离运行在它们自己的进程中。CDC MVM当前仅仅在linux和solaris下可用,因为这有这些系统提供叉子(fork)兼容。Java级的隔离是进程自动掌握的。

    象DoS攻击应付资源缺乏,CDC VM应用系统去执行配额假如需要。这没有什么不同和本地程序对应资源缺乏的解决方法来说。

可靠的终止

CLDC 隔离终止

       CLDC MVM提供自己的线程库。当它需要终止程序时,它简单的选择不计划更多的程序,并取消所有的参考来隔离。因为所有的程序都是100%的纯java代码,它们的资源会通过垃圾回收(garbage collector)来清空。一些资源有本地副本,这些会被那些由GC触发的私有终结器(finalizers)来清空。注意:终结器不是CLDC规格的一部分。它是一个VM提供的一个被系统库使用的执行。

CDC 隔离终止

       CDC MVM里的隔离有自己的进程,终止是简单的杀死各自的进程。

效率

CLDC 普通资源共享

    CLDC MVM,所有的隔离作为线程运行在同一个物理VM,它们自动共享所有常量数据。这包括类的元数据(metadata)(比如常量池,属性),method bytecodes,ROMized对象和内部字符串。

    每个隔离从java堆中得到一个配额。通常,从java堆中分配的资源不是共享的。无路如何,这儿有更少的内部破碎因为分配的隔离来自同一个堆。

    开始一个新的隔离不需要VM完全重初始化。无论如何,系统库的类初始化需要重运行。

CDC 普通资源共享

       CDC MVM,隔离是从一个共同的进程分离(fork)出来的不同的进程,它们共享作为数据的只读内存页,不需要写入。CDC MVM采用几种技术来帮助系统最大化共享。

    在CDC MVM开始一个新隔离也不需要child VM完全重新初始化,但工作线程需要重生因为进程分离(fork)不和线程工作。象CLDC,类的初始化也需要重运行。

CDC MVM是真的MVM

       如果CDC MVM应用它的很多特性在系统进程,它和其他简单运行完整CDC VM的不同实例在进程中有什么不同?MVM被优化来允许有效的资源共享,但单个完整的VM做不到。这些优化也导致了一些性能上的开销但不会在单个完整VM上。什么?MVM有性能开销?为什么?是的,你想它会免费?你在2个隔离之间共享更多,开销也会更多。反过来也一样。但通常开销只有23%(如果我记得正确)。这可能或不可能成为你需要性能上的重要依靠。

       MVM和程序管理器一起来管理隔离,和单个完整VM运行在不同进程不一样,你有你自己的进程。

       在练习中,不是所有的隔离完全可用因为系统进程也会使用。比如,一些物理资源(硬件和系统)不能复制。因此,访问这些资源需要在隔离之间协调以至于它们不能相互一起。一个例子是绘画屏。Java类库(或他们的本地代码)需要修改来允许隔离间的协调。这个协调有程序管理器控制。

       物理资源的协调由CLDC MVM来执行。

我能在CDC中使用CLDC风格的MVM吗?

       技术上可以。。。但是这儿有个问题。CLDC风格的MVM不能做关于本地代码隔离的事情。那么,让我们看看我们只需要在环境中配置VM在我们保证那儿没有本地代码在程序中(或者我们拒绝有本地状态的程序)。那么我们能使用CLDC风格的MVM了吗?

       是的,但这儿仍然充满了附加的挑战。CDCVMCVM)运行在完全pre-emptive的本地线程上。调度程序在系统中,不是VM。因此,VM在线程调度上没有控制权(如果系统不提供这个机制。。。通常,他们不提供)。因此,近似于CLDC不能进行可靠的终止工作。

       我们还记得老的Thread.stop()API。但我们已经知道它有一个问题。但是,我们在这儿谈论的是100%的纯java代码程序。我们说我们不会让任何的本地代码运行。我们有CLDC风格的MVM来隔离java状态。这会让Thread.stop()的问题走开吗?现在让我们的任务终止?

       是的,我们可以让它工作。VM可以设置一个标志来在隔离中指出一个终止状态来终止。检查这个终止状态需要插入一系列的地方:VM InterpreterJIT编译器生产的编译后代码,本地代码中的循环。这些保证线程被终止

       在这些检查点,如果终止状态被检测到,一个未捕获的异常将被抛出。VM会忽略try-catch块当它出来这个异常。这确保程序不能捕获异常并防碍线程被终止。异常会导致java和本地堆栈展开。

       VM能够展开堆栈框架为java bytecode methods。但本地方法会要求本地方法检测异常并返回。因此我们不允许任何程序的本地代码,我们只在系统库中处理本地代码。每个本地方法会被修改来检测终止状态,确保它作为异常的替代返回

       另外,所有本地方法需要被检查以确保他们释放了所有的由他们方法中分配的资源。这些资源必须被清空在方法返回异常堆栈展开前。一些本地资源没有被分配和使用在一些本地方法。对这些类型的资源,我们需要确保他们同finalizer java对象的关联,因此他们能被取回当对应的java对象被GC时。

为什么我不能有自己的CLDC风格的MVMCDC

       你是不是感到疲惫?我们需要做这么多步骤为了在CDC中执行CLDC风格的MVM无疑是冗长的。现在考虑CDC库同CLDC库的复杂度和大小。因为所有本地库将被适当的修改来迎合CLDC风格的MVM正确的工作。这个执行努力是相当的高。风险也相当的高。

       另外,如果VM部署在一些中间设备堆(例如对机定盒的MHP中间设备),所有这些中间设备的本地代码也需要同样的处理来保证CLDC风格的MVM正确工作。

       因此,这个风格的MVMCDC来说执行难度是相当的高。Sun究竟有没有试验代码在CVM上来执行这种MMV。因为执行的花费和风险包含本地代码部分,这些代码从来没被完成。但是,它和快会被开原在下一个phoneMe Advanced版本,欢迎每一个感兴趣的人来完善它。

JavaSEMVM

       CDC风格的MVM可能或不可能在JavaSE中运用。这些VM的结构不同。但是,他们可能或不可能有利于这个途径。JavaSE VM的类共享特性已经是这个方向的一步了。

       CLDC风格的MVM。如果你明白它同CDC的不同,你可以看到它已经很大程度上接近JavaSE了。这是因为JavaSE有相当大的类库。但是,只有这些库中的本地代码会被贡献不同。幸好,JavaSE库的大部分是纯java代码,但这足够让这个任务较少的让人畏惧。

如果我不需要所有的隔离特性?

       CDC,你已经有命名空间隔离通过使用类装载器(classloader)。这儿有部署在基于同步线程和类装载器命名空间隔离的系统。但是,这些系统经受不同的缺点来自真隔离的。一个通常的抱怨是一个行为不当的程序能够坠毁整个系统,所有的程序都同它一起坠毁。

       已经有厂商提供MVM解决方案。我总是希望他们能做一些工作象Sun在分析问题,或者他们提供一部分的解决方案。但是我没有使用他们的VM,我不知道他们的MVM是怎样完成的。

这对你意味着什么?

我希望你现在理解这个令人畏惧的任务包含在建立MVM解决方案中。我明白当你需要MVM的时候它还不可用,知道去执行这个这是很困难的。

但是,现在java平台开源了,如果这些特性对你来说非常重要,请贡献在VM领域。如果我们为这个工作在一起一点时间,我们能让MVM更快实现。

祝有美好的一天!

2007年08月06日

   

Mark Lam has been a virtual machine engineer in the JavaME CDC team at Sun Microsystems for over 6 years. Before joining Sun, he was a real-time embedded systems developer for 6+ years, working on application frameworks, graphics systems, networking protocols, game development, and fault tolerant systems amongst other things, on devices ranging from 64KB 8bit uControllers to 32-bit RISC machines.

原文URLhttp://weblogs.java.net/blog/mlam/archive/2006/11/c_further_with.html#more

         我已经谈论过一些关于CVM的深奥的知识,并且我想是时候说一些真正的技术了。因此,我花了昨天大部分的时间来制作了一个关于CVM的结构图来向你展示它的结构,但它比我想象的要长。结果,昨天没有放上blog。今天有希望能把它完成,并在周一补写到blog。它看来像放在坚果里的CVM

       另外,我使用 InkScape 来存放我的CVM结构图,我不知道这是不是最好的,但它适合做这个工作。因此,我想我在这给一个说明万一其它人也在找这样的工具。我使用 InkScape 是因为我在SVG里渲染CVM结构图,因此我需要缩放它来匹配任意你需要的解决方案而不用考虑细节。但我也发现我的浏览器不能快速的显示SVG格式的图片(可能我没有导出正确的格式),如果有人发现你的浏览器不支持SVG格式,请告诉我,我会把它转成PDF格式。

       顺带说一下,我想感谢留言的2位朋友,这让我知道我没有对牛弹琴。

为什么用C来写CVM

       这是CC++之间的选择?像我在前面文章里指出的一样,CVM的体系非常匹配对象类。使用C++已经是一个选择。我们选择C的原因是因为C编译器比C++编译器在嵌入式领域有良好的实用性。我提醒你可移植性是CVM的一个主要目标,也是为什么它在嵌入式领域能够生存的原因。有代表性的,当硬件更新,它总是和C编译器一起的,而C++编译器可能会或者可能不会更新。我们希望Java平台能够在任何平台运行,事实上这和选择C是一样的。

       第二个要注意的地方, 我们发现一些C++编译器总是生成非常低效的代码在一个周期(23个周期),这对任何嵌入式软件都是不好的。现在,在你跳到结束前,我不认为C++原因本身是低效的。私下的,我是一个C++的爱好者,我知道它怎样让你写出真正一流和高效的代码(像计算编译器协作)。或者臃肿的代码。我的建议是在这个时候,一般的人们不要过多的关心C++在给了你多少的工具链(和C比)。不要说任何C++比其它好很多的话。事实上,C++有一个坏名字,我想这非常不幸。

       照顾好自己,7年前,CVM就决心做些什么。低效的代码生成在34年前被察觉,或许这些关于实用性和效率的问题已经被修复了。

       其它我想要说的关于便携性和性能的在下面

便携性的一个注解

       我在前面提过,CVM的代码是由在shared目录里最高的代码密度组织成的(相对于特殊平台)。给你一个特别的概念,在2003年,我们在shared目录和platform目录为动态适应编译(JIT)测量代码线作对比。在这种情况下,shared部分包括 RISC层在 portlibs 目录,开始共享代码的比率是80-90%,保留CPUOS的特别代码。大多数由汇编代码组成的非共享代码生成的特定CPU体系的常规程序。如果我们考虑VM空闲因素,这个对共享代码的比率会更高。我们没有实际测量这个,但如果我乱猜的话,我说他会上升到95%

       一个汇编程序非常小的子集作为编译器代码和VM休眠之间的过渡是非常必要的。保持所有的焦点在特殊CPU体系的优化上。一个好消息是,除了少量的粘合代码外,CPU特殊的优化代码都是可选的。这意味着你不需要为JIT执行所有的命令来保证操作的正确。代码的共享部分(小范围汇编代码)对性能的提升仍然有重大的意义(50~70%的提升)。附加的优化根据需要或VM开发者的时间

       另一个要注意的问题是关于CVM体系的。一个 VM开发者必须能够完成一个CVM的完全最小效果的移植,但仍然会得到许多性能上的原因。那么,如果时间允许,尽可能的调整和优化以得到附加性能的提升。这就是为什么我们大多数的更新和提升在用C写的共享代码中。

       提升仅仅是一个CVM build的解释(比如禁止JIT),它会带来少许的成就。这儿只有一个常规汇编程序需要编写。这个程序通常被称为“本地调用(invokeNative)”,它的责任是成为本地代码和解释器的粘合器。它的全名是“(CVMjniInvokeNative)”。在这儿有例子。如果你做linux移植(或其他系统),那么你会有个很好的开始。如果不是,你将执行HPI,但你可以使用其他已经存在移植的相关代码。

       总的说来,CVM容易移植不仅仅依靠减少必须进行的移植工作量。(共享代码对平台特殊代码的高比例),它的目标总是允许移植成就相对于现阶段。对开发者来说,为了适合开始时间它是很容易分离的。它总是增加系统的易测性。

性能的一个注解

       我的职业教育背景是硬件。因此,随时我听到的关于某些软件的创新都会让我开始提高系统的性能。怎样的软件能使硬件超过它现在的性能运行的更快?答案是未知的。

       任何软件能取得的最快速度是作为运行在硬件上的命令或限制。无论如何,硬件靠自己来提升性能不是那么容易的。他让软件指挥(direct)硬件来有效的工作。“指挥(direct)”翻译成管理成本(management cost),我们通常不喜欢为不必要的东西买单。因此,用软件来提升性能相对更好的硬件是一个很好的主意。这让我们花费较少的代价来取得更多的性能。

       管理成本(management cost)是影响代码的移植性和可维护性。代码的可维护性、易移植性这个话题不需要太多的解释对于那些每年有很多维护经费的开发者。

       CVM代码的可移植性、可维护性总是非常重要的。现在phoneMe开源了,每个人都能够贡献bug 修改(fixes)和增加(enhancement)。我想提醒大家代码需要坚持维护,这对每个人都非常重要。如果开源的一段代码没一个人能读懂是怎么一回事??这可能用密匙来加密。我说丢弃因为代码恢复往往是恢复甚至是有时作者已经通过。(完全翻不通:After all, how open-source is a piece of code if no one can understand it? It might as well have been encrypted with the secret key thrown away. I say thrown away because unmaintainable code also tend to be unmaintainable even to its creators once some time has passed.

       关键点是我们不需要牺牲可维护性在性能上。这是一个谬论,当我们不能在同一时间拥有高性能和可维护的代码。非常难得的是有异常在我们可维护的代码中。无论怎样,我们应该包含损害不至于通过基础代码影响本地化。(The point is that we shouldn’t sacrifice maintainability in the name of performance. It is a fallacy to think that we can’t have both high performance and maintainable code at the same time. Very rarely will there be an exception where we might have to sacrifice some maintainability. Even then, we should try to contain the damage so that it is localized and not wide spread across the code base.

       这是CVM的解释程序循环用C写而不是汇编的一个原因。这的确可以用汇编来写,但代价是巨大的,在你移植和维护的时候。在实践中,我们发现CVMC写的解释程序循环的效率和优化的汇编循环差别不大,但移植性和可维护性很好。

结束

谢谢大家听我讲了这么多,下次将介绍CVM的世界,子系统,数据结构,还有一张大图片

祝有美好的一天!