JVM中的方法内联

在C++中,可以明确定义内联函数,使用inline关键字。在Java中不能定义内联函数,但是方法的内联在JIT编译中还是存在的,只不过是JIT自动优化的,我们无法在写代码的时候指定。

所谓内联函数就是指函数在被调用的地方直接展开,编译器在调用时不用像一般函数那样,参数压栈,返回时参数出栈以及资源释放等,这样提高了程序执行速度。 一般函数的调用时,JVM会自动新建一个堆栈框架来处理参数和下一条指令的地址,当执行完函数调用后再撤销该堆栈。

写一段代码来测试一下。

public class Test {

public static void foo() {

boolean t = true;

boolean f = false;

System.out.println(t == f);

}

public static void main(String[] args) {

foo();

}

}

看一下这段代码的字节码信息,使用命令

javap -c -s -l -verbose Test

关键部位的字节码信息如下

public static void foo();

Signature: ()V

LineNumberTable:

line 5: 0

line 6: 2

line 7: 4

line 8: 20

LocalVariableTable:

Start Length Slot Name Signature

2 19 0 t Z

4 17 1 f Z

Code:

Stack=3, Locals=2, Args_size=0

0: iconst_1

1: istore_0

2: iconst_0

3: istore_1

4: getstatic #15; //Field java/lang/System.out:Ljava/io/PrintStream;

7: iload_0

8: iload_1

9: if_icmpne 16

12: iconst_1

13: goto 17

16: iconst_0

17: invokevirtual #21; //Method java/io/PrintStream.println:(Z)V

20: return

LineNumberTable:

line 5: 0

line 6: 2

line 7: 4

line 8: 20

LocalVariableTable:

Start Length Slot Name Signature

2 19 0 t Z

4 17 1 f Z

StackMapTable: number_of_entries = 2

frame_type = 255 /* full_frame */

offset_delta = 16

locals = [ int, int ]

stack = [ class java/io/PrintStream ]

frame_type = 255 /* full_frame */

offset_delta = 0

locals = [ int, int ]

stack = [ class java/io/PrintStream, int ]

public static void main(java.lang.String[]);

Signature: ([Ljava/lang/String;)V

LineNumberTable:

line 11: 0

line 12: 3

LocalVariableTable:

Start Length Slot Name Signature

0 4 0 args [Ljava/lang/String;

Code:

Stack=0, Locals=1, Args_size=1

0: invokestatic #33; //Method foo:()V

3: return

LineNumberTable:

line 11: 0

line 12: 3

LocalVariableTable:

Start Length Slot Name Signature

0 4 0 args [Ljava/lang/String;

可以看到在字节码中是不能反映出方法内联的,方法内联是在JIT编译时发生的,Oracle对方法内联的举例(传送门:http://java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html),JVM会做出优化。要发现该方法是否被内联可以使用如下命令:

java -Xrunhprof:cpu=times InlineMe

在本文中我没有进行这个测试,如果想看这个测试结果,可以到这里来看。(传送门:http://spring8314.iteye.com/blog/139299)

也可以使用参数:(这个参数只能在debug mode下使用,可以参见http://www.oracle.com/technetwork/java/javase/tech/exactoptions-jsp-141536.html,建议使用fastdebug来玩,传送门:http://agapple.iteye.com/blog/1056599)

-XX:+PrintInlining

打印出来的内容如下

@ 0 org.dothwinds.test.Test::foo (21 bytes)

可以看到log中存在foo方法。后面的21字节表示字节码所占用的字节。那么用javap可以看到foo方法的字节码正好占用21字节

Code:

Stack=3, Locals=2, Args_size=0

0: iconst_1

1: istore_0

2: iconst_0

3: istore_1

4: getstatic #15; //Field java/lang/System.out:Ljava/io/PrintStream;

7: iload_0

8: iload_1

9: if_icmpne 16

12: iconst_1

13: goto 17

16: iconst_0

17: invokevirtual #21; //Method java/io/PrintStream.println:(Z)V

20: return

我们将这段代码反汇编x86asm来看看。

java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+Pr

intAssembly org/dothwinds/test/Test >log.log

到当前目录下找到这个log文件,将有意义的代码提取出来。

Code:

[Disassembling for mach='i386']

[Entry Point]

[Verified Entry Point]

[Constants]

# {method} 'main' '([Ljava/lang/String;)V' in 'org/dothwinds/test/Test'

# parm0: ecx = '[Ljava/lang/String;'

# [sp+0x20] (sp of caller)

;; block B6 [0, 0]

0x01cd3930: mov %eax,-0x8000(%esp)

0x01cd3937: push %ebp

0x01cd3938: sub $0x18,%esp ;*invokestatic foo

; - org.dothwinds.test.Test::main@0 (line 11)

;; block B0 [0, 3]

;; 10 move [obj:0x0|L] [edx|L] [patch_normal] [bci:4]

0x01cd393b: nop

0x01cd393c: nop

0x01cd393d: nop

0x01cd393e: nop

0x01cd393f: nop

0x01cd3940: jmp 0x01cd3990 ; {no_reloc}

;; 12 move [Base:[edx|L] Disp: 2147483647|L] [ecx|L] [patch_normal] [bci:4]

0x01cd3945: nop

0x01cd3946: nop

0x01cd3947: nop

0x01cd3948: jmp 0x01cd39bb ; implicit exception: dispatches to 0x01cd399a

0x01cd394d: nop ;*getstatic out

; - org.dothwinds.test.Test::foo@4 (line 7)

; - org.dothwinds.test.Test::main@0 (line 11)

0x01cd394e: cmp (%ecx),%eax ; implicit exception: dispatches to 0x01cd39c5

0x01cd3950: mov $0x0,%edx ;*invokevirtual println

; - org.dothwinds.test.Test::foo@17 (line 7)

; - org.dothwinds.test.Test::main@0 (line 11)

0x01cd3955: nop

0x01cd3956: mov $0xffffffff,%eax ; {oop(NULL)}

0x01cd395b: call 0x01c0b210 ; OopMap{off=48}

;*invokevirtual println

; - org.dothwinds.test.Test::foo@17 (line 7)

; - org.dothwinds.test.Test::main@0 (line 11)

; {virtual_call}

0x01cd3960: add $0x18,%esp

0x01cd3963: pop %ebp

0x01cd3964: test %eax,0x260100 ; {poll_return}

0x01cd396a: ret

这段代码之所以能看出有内联特征是因为(借用撒迦(http://rednaxelafx.iteye.com/)的话):

0x01cd395b: call 0x01c0b210 ; OopMap{off=48}

;*invokevirtual println

; - org.dothwinds.test.Test::foo@17 (line 7)

; - org.dothwinds.test.Test::main@0 (line 11)

; {virtual_call}

每行右边分号后面是注释。它会显示当前机器指令对应的原本的Java字节码是什么、字节码是从哪里来的。

可以看到这里call指令对应的字节码原本是个invokevirtual,而它原本是在foo()方法中的,并且被main()方法内联了。

如果不想进行方法内联,可以使用参数:

-XX:CompileCommand=dontinline,org/dothwinds/test/Test,foo

但是需要注意的是,C1(Client模式)下是不检查dontinline的,解决方法可以见帖子:传送门(http://hllvm.group.iteye.com/group/topic/26381)

如果嫌这种方式麻烦,那只能使用C2(Server模式)来玩了。

最后要十分感谢撒迦的帮助和指点。

java内联函数,JVM中的步骤内联相关推荐

  1. 内联函数 —— C 中关键字 inline 用法解析(转载)

    一.什么是内联函数 在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗. 为了解决这个问题,特别的引入了inline修饰符,表示为内联函数. 栈空间就是指放 ...

  2. 内联函数什么时候展开_内联函数

    内联函数什么时候展开 You know all of those Util files you create with all sorts of small functions that you en ...

  3. C++ 内联函数详解(搞清内联的本质及用法)

    目录 一.什么是内联函数 1.直观上定义: 2.更深入的思考: 二.为什么使用内联函数 1.为什么要代替部分宏定义 2.普通函数频繁调用的过程消耗栈空间 3.更深入的思考 三.内联函数和编译过程的相爱 ...

  4. 内联函数有什么优点?内联函数和宏定义的区别

    用内联函数取代宏: 1.内联函数在运行时可调试,而宏定义不可以; 2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定 义则不会: 3.内联函数可以访问类的成员变量,宏定义则 ...

  5. python sort函数返回值_lambda函数与箭头函数在集合内置函数应用中的对照学习

    Python语言中有一个定义轻量级规则的lambda函数,其语法格式为: Lambda 参数列表:返回值表达式 简单的例子如:定义func=lambda x,y:x+y,则调用func(10,20)的 ...

  6. python中比较重要的几个函数_Python 几个重要的内置函数 python中的内置函数和关键字需要背过吗...

    python重要的几个内置函数用法 python内置函数什么用忘不掉的是回忆,继续的是生活,错过的,就当是路过吧.来来往往身边出现很多人,总有一个位置,一直没有变.看看温暖的阳光,偶尔还是会想一想. ...

  7. java stw_快速理解Java垃圾回收和jvm中的stw

    Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外).Java中一种全局暂停现象,全局停顿,所有Java代码停 ...

  8. 内联函数 —— C 中关键字 inline 用法解析

    一.什么是内联函数 在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗. 为了解决这个问题,特别的引入了inline修饰符,表示为内联函数. 栈空间就是指放 ...

  9. java log日志函数_Java 中的 AWS Lambda 函数日志记录 - AWS Lambda

    AWS 文档中描述的 AWS 服务或功能可能因区域而异.要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门. 本文属于机器翻译版本.若本译文内容与英语原文存在差异,则一律以英文原文为准. Ja ...

最新文章

  1. python官网下载速度慢-python 官网慢
  2. 如何访问自定义键值的二维数组
  3. HDU 1248 寒冰王座
  4. “停课不停学”,线上填报如何逼疯老师
  5. 干货 | Elasticsearch7.X Scripting脚本使用详解
  6. String s = new String(“hello“)创建了几个对象(图解)
  7. linux ll命令时间,linux ll显示时间格式
  8. word vba 点击任意域代码,刷新整个文档的域代码值
  9. ip对应的区域查询(asp.net版)
  10. Java动态绑定与多态
  11. 如何删除win10的windows.old
  12. 毕业设计-----------学生选课管理系统的设计
  13. 物流配送管理系统(ssm,mysql)
  14. arcgis图层合并裁剪
  15. 国内免备案服务器有哪些?
  16. MAE 自监督算法介绍和基于 EasyCV 的复现
  17. Matlab 拉丁超立方采样lhsdesign函数、lhsnorm函数介绍
  18. 无法启动因为计算机丢失ac1st16,Win7启动CAD时提示丢失ac1st16.dll如何修复
  19. Jupyter Notebook切换conda虚拟环境
  20. python编程语言介绍-编程语言及python介绍

热门文章

  1. 最基础的python自动化测试(基础篇)
  2. 学生成绩管理系统的设计-实践周作业
  3. 微信小程序js.json.wxss.wxml后缀文件的区别及使用方法
  4. 确认合同无效请求权,属于形成权
  5. Vue基础入门(下)
  6. 任务分配问题关于医院的一些基础类的创建
  7. (转)一个房奴的精神大字报
  8. smartrcp状态栏的实现示例
  9. [分享] 软件配置管理经典面试题
  10. 论文一篇-软件工程未来的发展