`
totoxian
  • 浏览: 1075003 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

java编译器对finally的优化

阅读更多
<Inside JVM>是市面上少数几本系统介绍JVM的书籍,(作者Bill Venners的网站artima也是非常有趣的Java技术社区),这本书最有意思的地方可能就是作者为了演示JVM内部工作原理编写的Applet, 非常值得向大家推荐。<Inside JVM>最新的是第二版,也有些年头了,尽管JVM规范本身的变化并不大,但是java编译器的有些细节处理还是跟书里面不一样了,比如说现在的java编译器一般都会对finally作inline处理。

这个Applet演示的是下面这段程序的bytecode。
代码1:
/*
*copiedfrom
http://www.artima.com/insidejvm/applets/HopAround.html
*/

classClown...{

staticinthopAround()...{
inti=0;
while(true)...{
try...{
try...{
i
=1;
}
finally...{//Thefirstfinallyclause
i=2;
}
i
=3;
//Thisreturnnevercompletes,becauseof
//thecontinueinthesecondfinallyclause
returni;
}
finally...{//Thesecondfinallyclause
if(i==3)...{
//Thiscontinueoverridesthereturnstatement
continue;
}
}
}
}
}


其主要目的演示的是finally子句的处理, 在Inside JVM和JVM Spec的7.13节当中,finally都是通过jsr和ret指令实现的,按照书中的说法,hopAround()方法将被编译成47个jvm指令,如下所示:

代码2:
/**//*
*copiedfrom
http://www.artima.com/insidejvm/applets/HopAround.html
*/


0iconst_0//Pushconstant0
1istore_0//Popintolocalvar0:inti=0;

//Bothtryblocksstarthere(seeexceptiontable,below):
2iconst_1//Pushconstant1
3istore_0//Popintolocalvar0:i=1;
4jsr18//Jumptomini-subroutineatoffset18(the
//firstfinallyclause)
7goto24//Jumptooffset24(tojustbelowfirst
//finallyclause)

//Catchclauseforthefirstfinallyclause:
10astore4//Popthereferencetothrownexception,store
//inlocalvariable4
12jsr18//Jumptomini-subroutineatoffset18(the
//firstfinallyclause)
15aload4//Pushthereference(tothrownexception)
//fromlocalvariable4
17athrow//Rethrowthesameexception

//Thefirstfinallyclause:
18astore5//Storethereturnaddressinlocalvariable5
20iconst_2//Pushconstant2
21istore_0//Popintolocalvar0:i=2;
22ret5//Jumptoreturnaddressstoredinlocalvariable5

//Bytecodesforthecodejustafterthefirstfinallyclause:
24iconst_3//Pushconstant3
25istore_0//Popintolocalvar0:inti=3;

//Bytecodesforthereturnstatment:
26iload_0//Pushtheintfromlocal
//variable0(i,whichis3)
27istore_1//Popandstoretheintintolocal
//variable1(thereturnvalue,i)
28jsr39//Jumptomini-subroutineatoffset39(the
//secondfinallyclause)
31iload_1//Pushtheintfromlocalvariable1(the
//returnvalue)
32ireturn//Returntheintonthetopofthestack

//Catchclauseforthesecondfinallyclause:
33astore_2//Popthereferencetothrownexception,store
//inlocalvariable2
34jsr39//Jumptomini-subroutineatoffset39(the
//secondfinallyclause)
37aload_2//Pushthereference(tothrownexception)
//fromlocalvariable2
38athrow//Rethrowthesameexception

//Thesecondfinallyclause:
39astore_3//Storethereturnaddressinlocalvariable3
40iload_0//Pushtheintfromlocalvariable0(i)
41iconst_3//Pushconstant3
42if_icmpeq47//Ifthetoptwointsonthestackareequal,jump
//tooffset47:if(i==3){
45ret3//Jumptoreturnaddressstoredinlocalvariable3
47goto2//Jumptooffset2(thetopofthewhile
//block):continue;

但是如果用Sun JDK 1.4之后的编译器编译,这段程序实际上被编译成截然不同的38个jvm指令(如下所示),其主要区别是finally 子句被inline到了正常代码和exception handling代码,避免了jsr/ret, 从而减少了JSR跳转/pop出栈/ret返回三条指令,在本例中,不但提高了运行效率,还减少了class文件的大小,其代价是finally block之中的内容被复制到了两个地方,如果finally block的指令很多,class文件可能会变大。

代码3:
staticinthopAround();
Code:
0:iconst_0//pushconst0
1:istore_0//popintolocalvar0:i=0

//startbothtryblock
2:iconst_1//pushconst1
3:istore_0//popintoi

//startfirstfinallyblock(itisinlined)
4:iconst_2//pushconst2
5:istore_0//popintolocalvar0(i)
6:goto14//skipexceptionhandling

//startexceptionhandlingforsecondtryblock
9:astore_1//popthrownexceptionreferenceintolocalvar1
//startfirstfinallyblock(inlineagain)
10:iconst_2//pushconst2
11:istore_0//popinto0
12:aload_1//pushthereference(theexception)fromlocalvar1
13:athrow//throwexeption

//goonthefirsttryblock
14:iconst_3//pushconst3
15:istore_0//popintolocalvar0(i)
16:iload_0//pushvaluefromlocalvar0(i)
17:istore_1//popintolocalvar1(returnvalue)

//startsecondfinallyblock(inlined)
18:iload_0//pushvaluefromlocalvar0(i)
19:iconst_3//pushconst3
20:if_icmpne26//ifthetoptwointsonthestackarenotequal,jumpto26
23:goto2//loop

//goonthefirsttryblock
26:iload_1//pushvaluefromlocalvar1(returnvalue)
27:ireturn//returnmethod

//startexceptionhandlingforfirsttryblock
28:astore_2//popthrownexceptionreferenceintolocalvar2
//startsecondfinallyblock(inlinedagain)
29:iload_0//pushvaluefromlocalvar0(i)
30:iconst_3//pushconst3
31:if_icmpne37//ifthetoptwointsonthestackarenotequal,jumpto37
34:goto2//loop
37:aload_2//pushthereference(theexception)fromlocalvar2
38:athrow//throwexception

Exceptiontable:
fromtotargettype
249any
9109any
21828any
282928any



Eclipse编译器显然也将finally inline了,Eclipse 3.2甚至disable了编译器不inline finally的选项,ECJ编译出来的hopAround()由39条指令组成:

代码4:
staticinthopAround();
Code:
0:iconst_0
1:istore_0

//startbothtry
2:iconst_1
3:istore_0

//skipexceptionhandling
4:goto12

//exceptionhandlingforfirsttryblock
7:astore_1
//firstfinallyblockinlined
8:iconst_2
9:istore_0
10:aload_1
11:athrow

//firstfinallyblockinlinedagain
12:iconst_2
13:istore_0

//goonsecondtryblock
14:iconst_3
15:istore_0
16:iload_0
17:istore_3

//secondfinallyblockinlined
18:iload_0
19:iconst_3
20:if_icmpne26
23:goto2

//return
26:iload_3
27:ireturn

//exceptionhandlingforsecondtry
28:astore_2
//secondefinallyinlined
29:iload_0
30:iconst_3
31:if_icmpne37
34:goto2
37:aload_2
38:athrow

//whatdoesthismean?
39:goto2
Exceptiontable:
fromtotargettype
277any
21828any


区别不大,Eclipse编译器将exception handling的代码放在了正常路径的finally block之前,多出来的一句是第39句goto 2, 比较匪夷所思, 因为这一句显然是无法被执行到的。还有一个区别是exception table缩减为两项,少掉的两条是针对exception handling代码自身的,Sun javac加上的这两条意欲何为也有点让人迷惑...也许因为这个原因,ECJ编译出来的class要稍微小一些(对于这个class, 222bytes vs. 235bytes)。

这两种主流Java编译器都对finally做了inline处理,所以我们在平常写java代码的时候应该注意在try/catch/finally里面不要做太多的分支,并且finally block中包含的内容尽量不要太多,否则class文件可能会多占用不少空间。

还有一个tips: javac和Eclipse编译器缺省都带有debug信息,加上-g:none选项之后,class文件要小一半,对于Eclipse编译器尤其明显。

最后一个tips送给有耐心读到这里的人: 请代码3中的14-17行,在进入finally block之前,JVM将把变量i当前的值存入另外一个本地变量(istore_1),并且在26-27行将此值返回,所以如果在finally block中修改了i的值,对返回值是没有影响的,如果要将修改过的i返回,请在finally中直接return i。更多finally实现的细节请参考Inside JVM:).
分享到:
评论

相关推荐

    java代码性能优化23种技巧

    对于只读属性或者不需要修改的方法,将其标记为`final`可以提高编译器对代码的理解,并可能进一步优化。 **优化建议**: 对于简单的getter和setter方法,如果确定这些方法不会被子类覆盖,则应考虑将其标记为`final`...

    java开发性能优化

    4. **利用final修饰符**:final关键字可以提高性能,因为编译器可能会对final方法进行内联,减少方法调用的开销。此外,final类和方法不能被覆盖,有助于保持代码稳定。 5. **优先使用局部变量**:局部变量在栈中...

    java代码优化大全

    在软件开发过程中,为了提升应用程序的性能,我们需要对Java代码进行优化。这是因为计算机系统的资源(如内存、CPU时间、网络带宽等)是有限的,而优化的目标就是使程序能够以最小的资源消耗完成既定任务。优化通常...

    Java性能优化的45个细节.pdf

    以下是关于Java性能优化的详细知识点,内容包括单例模式的合理应用、静态变量的使用考量、对象创建与垃圾回收策略、final修饰符与内联优化、局部变量与实例变量的性能差异、包装类型与基本类型的内存影响、同步机制...

    Java程序性能优化(23条).

    ### Java程序性能优化知识点 #### 一、避免在循环条件中使用复杂表达式 在Java程序中,尤其是在不做编译优化的情况下,如果在循环条件中使用了复杂的表达式,那么这个表达式会在每次循环时被重新计算。这种重复计算...

    java代码优化笔记

    在进行Java代码优化时,有多个方面需要考虑,本文档提供的是一系列详细的优化建议,涵盖了异常处理、资源管理、数据结构使用、性能提升等多个角度。首先,异常处理是代码优化的重要环节。不应该对所有异常都使用通用...

    java程序性能优化好文章

    根据提供的文件信息,本文将详细阐述Java程序性能优化的相关知识点。 一、循环条件中避免使用复杂表达式 在循环中,循环条件会被反复计算。如果循环条件复杂,会导致每次迭代都进行大量的计算,从而降低程序运行...

    java程序性能优化

    以上介绍了几种常见的Java程序性能优化技巧,包括避免在循环条件中使用复杂表达式、合理设置集合类的初始大小、在`finally`块中关闭资源、使用`System.arraycopy()`代替循环复制数组以及将简单的getter/setter方法...

    必会的40个Java代码优化细节.pdf

    22. **使用Java编译器提供的开关进行优化**:如开启逃逸分析,让编译器自动优化。 23. **利用JIT即时编译器**:JIT能在运行时对热点代码进行优化,提升性能。 优化Java代码是一个持续的过程,需要根据具体场景和...

    Java程序性能优化.doc

    对于不再改变的变量,声明为`final`可以提高效率,因为编译器可以做出优化,如常量折叠。 7. 考虑使用Java 8及更高版本提供的流API(Stream API),它们提供了并行处理的能力,可以利用多核CPU提升性能。 8. 对于...

    java代码优化细节总结1.0版本.zip

    Java代码优化是提升程序性能和效率的关键步骤,尤其是在大型企业级应用中,高效的代码能够显著降低服务器资源消耗,提高用户体验。这份"java代码优化细节总结1.0版本"的资料,旨在归纳和分享一些Java编程中的最佳...

    java 开发工具 jdk 1.4 免安装版

    JDK(Java Development Kit)是Oracle公司提供的用于开发Java应用程序的重要软件包,它包含了Java编译器、Java虚拟机(JVM)、Java类库以及各种开发和调试工具,是Java开发的基础。 JDK 1.4版本是Java历史上的一个...

    Java程序优化大全

    这样做的好处是,编译器可以对这些方法进行内联优化,从而提高程序的执行速度。 **错误示例**: ```java class MAF { public void setSize(int size) { _size = size; } private int _size; } ``` **正确...

    java-jdk1.7-jdk-7u80-windows-x64.zip

    Java JDK包含了Java编译器(javac)、Java虚拟机(JVM)、Java运行库以及各种开发工具,如Java文档生成器(javadoc)和性能分析器(jmap)等。 JDK 1.7,也被称为Java 7,是继Java 6之后的一个重大更新,它引入了...

    Java虚拟机规范(中文 翻译版 Java_SE_7).rar

    Java SE 7中的C1和C2编译器分别针对服务器和桌面环境进行了优化。 6. **动态类型与invokedynamic指令**:Java SE 7引入了新的invokedynamic指令,支持更高效的动态语言实现,如Groovy、JRuby等。 7. **并发与多...

    Java认证考试基础知识辅导Java程序性能优化

    ### Java认证考试基础知识辅导:Java程序性能优化 在软件开发领域,特别是在企业级应用中,Java作为一种广泛应用的编程语言,其程序性能优化是至关重要的环节。本文将从几个方面介绍如何进行Java程序性能优化,包括...

    java jdk 1.6 中文帮助文档

    JDK(Java Development Kit)是Oracle公司提供的用于开发Java应用程序的软件包,包含Java编译器、Java运行环境、调试工具和其他实用工具。Java JDK 1.6是Java平台标准版的一个特定版本,发布于2006年,它引入了许多...

    java JDK 实例开发宝典

    1. **Java编译器(javac)**:这是将源代码(.java文件)转换为字节码(.class文件)的工具,使得Java程序可以在Java虚拟机(JVM)上运行。 2. **Java运行环境(JRE)**:包含Java虚拟机、类库和必要的组件,用于...

    java 飞流直下的代码

    4. **编译器优化**:Java的Just-In-Time (JIT) 编译器可以将热点代码编译为原生机器码,提高运行速度。理解JIT的工作原理,可以帮助我们设计更利于优化的代码。 5. **设计模式**:诸如单例模式、工厂模式、观察者...

    Java+Puzzlers(中英文并且带源码)

    2. **编译器优化**:Java编译器有时会进行优化,这可能导致预期之外的行为。例如,空指针检查可能在某些情况下被优化掉,从而引发运行时错误。 3. **字符串操作**:Java的字符串是不可变的,这意味着对字符串的操作...

Global site tag (gtag.js) - Google Analytics