|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
轮性能微软曾做过一个例子,就是同一个项目用java和.net来作,结果开发周期,.net是java的一半,性能java是.net的十分之一,代码量java是.net的三倍。呵呵,这说明了什么,.net的全方位比java好。但是有的人说.net不能跨平台,这个问题我和我同学曾讨论过,都认为微软的.net很可能早都可以跨平台了,但是微软为了保护他们的操作系统,所以才没有推出跨平台的.net,只是推出了跨语言的.net,功能 尾递回转换能加速使用程序的速率,但不是一切的JVM城市做这类转换
良多算法用尾递回办法暗示会显得分外简明。编译器会主动把这类办法转换成轮回,以进步程序的功能。但在Java言语标准中,并没有请求必定要作这类转换,因而,并非一切的Java假造机(JVM)城市做这类转换。这就意味着在Java言语中接纳尾递回暗示大概招致伟大的内存占用,而这并非我们希冀的了局。EricAllen在本文中论述了静态编译将会坚持言语的语义,而静态编译则一般不会。他申明了为何这是一个主要成绩,并供应了一段代码来匡助判别您的立即(JIT)编译器是不是会在坚持言语语义的同时做尾递回代码转换。
尾递回及其转换
相称多的程序包括有轮回,这些轮回运转的工夫占了程序总运转工夫的很年夜一部分。这些轮回常常要重复更新不止一个变量,而每一个变量的更新又常常依附于别的变量的值。
假如把迭代当作是尾递回函数,那末,就能够把这些变量当作是函数的参数。复杂提示一下:假如一个挪用的前往值被作为挪用函数的值当即前往,那末,这个递回挪用就是尾递回;尾递回不用记着挪用时挪用函数的高低文。
因为这一特性,在尾递回函数和轮回之间有一个很好的对应干系:能够复杂地把每一个递回挪用看做是一个轮回的屡次迭代。但由于一切可变的参数值都一次传给了递回挪用,以是比起轮回来,在尾递回中能够更简单地失掉更新值。并且,难以利用的break语句也经常为函数的复杂前往所替换。
但在Java编程中,用这类体例暗示迭代将招致效力低下,由于大批的递回挪用有招致仓库溢出的伤害。
办理计划对照复杂:由于尾递回函数实践上只是编写轮回的一种更复杂的体例,以是就让编译器把它们主动转换成轮回情势。如许您就同时使用了这两种情势的长处。
可是,只管人人都熟知怎样把一个尾递回函数主动转换成一个复杂轮回,Java标准却不请求做这类转换。不作这类请求的缘故原由也许是:一般在面向对象的言语中,这类转换不克不及静态地举行。相反地,这类从尾递回函数到复杂轮回的转换必需由JIT编译器静态地举行。
要了解为何会是如许,思索上面一个失利的实验:在Integers集上,把Iterator中的元素相乘。
由于上面的程序中有一个毛病,以是在运转时会抛出一个非常。可是,就象在本专栏之前的很多文章中已论证的那样,一个程序抛出的准确非常(跟很棒的毛病范例标识符一样)关于找到毛病躲在程序的甚么中央并没有甚么匡助,我们也不想编译器以这类体例改动程序,以使编译的了局代码抛出一个分歧的非常。
清单1.一个把Integer集的Iterator中的元素相乘的失利实验
importjava.util.Iterator;
publicclassExample{
publicintproduct(Iteratori){
returnproductHelp(i,0);
}
intproductHelp(Iteratori,intaccumulator){
if(i.hasNext()){
returnproductHelp(i,accumulator*((Integer)i.next()).intValue());
}
else{
returnaccumulator;
}
}
}
注重product办法中的毛病。product办法经由过程把accumulator赋值为0挪用productHelp。它的值应为1。不然,在类Example的任何实例上挪用product都将发生0值,不论Iterator是甚么值。
假定这个毛病终究被更正了,但同时,类Example的一个子类也被创立了,如清单2所示:
清单2.试图捕获象清单1如许的不准确的挪用
importjava.util.*;
classExample{
publicintproduct(Iteratori){
returnproductHelp(i,1);
}
intproductHelp(Iteratori,intaccumulator){
if(i.hasNext()){
returnproductHelp(i,accumulator*((Integer)i.next()).intValue());
}
else{
returnaccumulator;
}
}
}
//And,inaseparatefile:
importjava.util.*;
publicclassExample2extendsExample{
intproductHelp(Iteratori,intaccumulator){
if(accumulator<1){
thrownewRuntimeException("accumulatortoproductHelpmustbe>=1");
}
else{
returnsuper.productHelp(i,accumulator);
}
}
publicstaticvoidmain(String[]args){
LinkedListl=newLinkedList();
l.add(newInteger(0));
newExample2().product(l.listIterator());
}
}
类Example2中的被掩盖的productHelp办法试图经由过程当accumulator小于“1”时抛出运转时非常来捕获对productHelp的不准确挪用。不幸的是,如许做将引进一个新的毛病。假如Iterator含有任何0值的实例,都将使productHelp在本身的递回挪用上溃散。
如今请注重,在类Example2的main办法中,创立了Example2的一个实例并挪用了它的product办法。因为传给这个办法的Iterator包括一个0,因而程序将溃散。
但是,您能够看到类Example的productHelp是严厉尾递回的。假定一个静态编译器想把这个办法的注释转换成一个轮回,如清单3所示:
清单3.静态编译不会优化尾挪用的一个示例
intproductHelp(Iteratori,intaccumulator){
while(i.hasNext()){
accumulator*=((Integer)i.next()).intValue();
}
returnaccumulator;
}
因而,最后对productHelp的挪用,了局成了对超类的办法的挪用。超办法将经由过程复杂地在iterator上轮回来盘算其了局。不会抛出任何非常。
用两个分歧的静态编译器来编译这段代码,了局是一个会抛出非常,而另外一个则不会,想一想这是何等让人感应狐疑。
您的JIT会做这类转换吗?
因而,如清单3中的示例所示,我们不克不及希冀静态编译器会在坚持言语语义的同时对Java代码实行尾递回转换。相反地,我们必需依托JIT举行的静态编译。JIT会不会做这类转换是取决于JVM。
要判别您的JIT会否转换尾递回的一个举措是编译并运转以下小测试类:
清单4.判别您的JIT可否转换尾递回
publicclassTailRecursionTest{
privatestaticintloop(inti){
returnloop(i);
}
publicstaticvoidmain(String[]args){
loop(0);
}
}
我们来思索一下这个类的loop办法。这个办法只是尽量长工夫地对本身作递回挪用。由于它永久不会前往,也不会以任何体例影响任何内部变量,因而如清单5所示交换其代码注释将保存程序的语义。
清单5.一个静态转换
publicclassTailRecursionTest{
privatestaticintloop(inti){
while(true){
}
}
publicstaticvoidmain(String[]args){
loop(0);
}
}
并且,现实上这也就是充足完美的编译器所做的转换。
假如您的JIT编译器把尾递回挪用转换成迭代,这个程序将无穷期地运转下往。它所需的内存很小,并且不会随工夫增添。
另外一方面,假如JIT不做这类转换,程序将会很快耗尽仓库空间并呈报一个仓库溢堕落误。
我在两个JavaSDK上运转这个程序,了局使人惊奇。在SUN公司的HotspotJVM(版本1.3)上运转时,发明Hotspot不实行这类转换。缺省设置下,在我的呆板上运转时,不到一秒钟仓库空间就被耗尽了。
另外一方面,程序在IBM的JVM(版本1.3)上咕噜噜运转时却没有任何成绩,这标明IBM的JVM以这类体例转换代码。
总结
记着:我们不克不及寄但愿于我们的代码会老是运转在会转换尾递回挪用的JVM上。因而,为了包管您的程序在一切JVM上都有得当的功能,您应一直勉力把那些最天然地切合尾递回形式的代码按迭代作风编写。
可是请注重:就象我们的示例所演示的那样,以这类体例转换代码时很简单引进毛病,不管是由野生仍是由软件来完成这类转换。
实在从网上发明如许的文章其实不难,但愿人人在碰到成绩时都能学会怎样处置
进修的历程就是学会进修办法的历程,岂非不是吗?
令人可喜的是java现在已经开源了,所以我想我上述的想法也许有一天会实现,因为java一直都是不断创新的语言,每次创新都会给我们惊喜,这也是我喜欢java的一个原因。 |
|