<Inside JVM>是市面上少数几本系统介绍JVM的书籍,(作者Bill Venners的网站
artima也是非常有趣的Java技术社区),这本书最有意思的地方可能就是作者为了演示JVM内部工作原理编写的
Applet, 非常值得向大家推荐。<Inside JVM>最新的是第二版,也有些年头了,尽管JVM规范本身的变化并不大,但是java编译器的有些细节处理还是跟书里面不一样了,比如说现在的java编译器一般都会对finally作inline处理。
这个
Applet演示的是下面这段程序的bytecode。
代码1:
/*
*copiedfromhttp://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:
/**//*
*copiedfromhttp://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:).
分享到:
相关推荐
对于只读属性或者不需要修改的方法,将其标记为`final`可以提高编译器对代码的理解,并可能进一步优化。 **优化建议**: 对于简单的getter和setter方法,如果确定这些方法不会被子类覆盖,则应考虑将其标记为`final`...
4. **利用final修饰符**:final关键字可以提高性能,因为编译器可能会对final方法进行内联,减少方法调用的开销。此外,final类和方法不能被覆盖,有助于保持代码稳定。 5. **优先使用局部变量**:局部变量在栈中...
在软件开发过程中,为了提升应用程序的性能,我们需要对Java代码进行优化。这是因为计算机系统的资源(如内存、CPU时间、网络带宽等)是有限的,而优化的目标就是使程序能够以最小的资源消耗完成既定任务。优化通常...
以下是关于Java性能优化的详细知识点,内容包括单例模式的合理应用、静态变量的使用考量、对象创建与垃圾回收策略、final修饰符与内联优化、局部变量与实例变量的性能差异、包装类型与基本类型的内存影响、同步机制...
### Java程序性能优化知识点 #### 一、避免在循环条件中使用复杂表达式 在Java程序中,尤其是在不做编译优化的情况下,如果在循环条件中使用了复杂的表达式,那么这个表达式会在每次循环时被重新计算。这种重复计算...
在进行Java代码优化时,有多个方面需要考虑,本文档提供的是一系列详细的优化建议,涵盖了异常处理、资源管理、数据结构使用、性能提升等多个角度。首先,异常处理是代码优化的重要环节。不应该对所有异常都使用通用...
根据提供的文件信息,本文将详细阐述Java程序性能优化的相关知识点。 一、循环条件中避免使用复杂表达式 在循环中,循环条件会被反复计算。如果循环条件复杂,会导致每次迭代都进行大量的计算,从而降低程序运行...
以上介绍了几种常见的Java程序性能优化技巧,包括避免在循环条件中使用复杂表达式、合理设置集合类的初始大小、在`finally`块中关闭资源、使用`System.arraycopy()`代替循环复制数组以及将简单的getter/setter方法...
22. **使用Java编译器提供的开关进行优化**:如开启逃逸分析,让编译器自动优化。 23. **利用JIT即时编译器**:JIT能在运行时对热点代码进行优化,提升性能。 优化Java代码是一个持续的过程,需要根据具体场景和...
对于不再改变的变量,声明为`final`可以提高效率,因为编译器可以做出优化,如常量折叠。 7. 考虑使用Java 8及更高版本提供的流API(Stream API),它们提供了并行处理的能力,可以利用多核CPU提升性能。 8. 对于...
Java代码优化是提升程序性能和效率的关键步骤,尤其是在大型企业级应用中,高效的代码能够显著降低服务器资源消耗,提高用户体验。这份"java代码优化细节总结1.0版本"的资料,旨在归纳和分享一些Java编程中的最佳...
JDK(Java Development Kit)是Oracle公司提供的用于开发Java应用程序的重要软件包,它包含了Java编译器、Java虚拟机(JVM)、Java类库以及各种开发和调试工具,是Java开发的基础。 JDK 1.4版本是Java历史上的一个...
这样做的好处是,编译器可以对这些方法进行内联优化,从而提高程序的执行速度。 **错误示例**: ```java class MAF { public void setSize(int size) { _size = size; } private int _size; } ``` **正确...
Java JDK包含了Java编译器(javac)、Java虚拟机(JVM)、Java运行库以及各种开发工具,如Java文档生成器(javadoc)和性能分析器(jmap)等。 JDK 1.7,也被称为Java 7,是继Java 6之后的一个重大更新,它引入了...
Java SE 7中的C1和C2编译器分别针对服务器和桌面环境进行了优化。 6. **动态类型与invokedynamic指令**:Java SE 7引入了新的invokedynamic指令,支持更高效的动态语言实现,如Groovy、JRuby等。 7. **并发与多...
### Java认证考试基础知识辅导:Java程序性能优化 在软件开发领域,特别是在企业级应用中,Java作为一种广泛应用的编程语言,其程序性能优化是至关重要的环节。本文将从几个方面介绍如何进行Java程序性能优化,包括...
JDK(Java Development Kit)是Oracle公司提供的用于开发Java应用程序的软件包,包含Java编译器、Java运行环境、调试工具和其他实用工具。Java JDK 1.6是Java平台标准版的一个特定版本,发布于2006年,它引入了许多...
1. **Java编译器(javac)**:这是将源代码(.java文件)转换为字节码(.class文件)的工具,使得Java程序可以在Java虚拟机(JVM)上运行。 2. **Java运行环境(JRE)**:包含Java虚拟机、类库和必要的组件,用于...
4. **编译器优化**:Java的Just-In-Time (JIT) 编译器可以将热点代码编译为原生机器码,提高运行速度。理解JIT的工作原理,可以帮助我们设计更利于优化的代码。 5. **设计模式**:诸如单例模式、工厂模式、观察者...
2. **编译器优化**:Java编译器有时会进行优化,这可能导致预期之外的行为。例如,空指针检查可能在某些情况下被优化掉,从而引发运行时错误。 3. **字符串操作**:Java的字符串是不可变的,这意味着对字符串的操作...