我今天正在教授一门入门编程课程,并且正在介绍一些涉及Java中变量赋值的简单代码。代码的重点不在于展示任何特别令人兴奋的东西,而主要是为了确保学生理解变量赋值语句。
我在电路板上有以下方法,并且一次只追踪一行:
private void simpleMethod() {
int myInt = 137;
myInt = 42;
myInt = myInt + 1;
/* ... code using myInt ... */
}
一名学生问我myInt
是否会在程序运行时实际保持值137和42,或者它是否会直接跳到持有43.我告诉学生代码将执行每一行转,所以变量实际上会保存这些中间值。
老实说,我不确定字节码javac
会发出什么(完全忽略JVM所做的优化)。法律允许javac
(或任何Java编译器)优化愚蠢的赋值语句,而只是直接将myInt
初始化为43?
根据javap
,在我的系统上,使用javac
编译的上述代码生成
0: sipush 137
3: istore_1
4: bipush 42
6: istore_1
7: iload_1
8: iconst_1
9: iadd
10: istore_1
11: return
所以这里没有优化。不过,我的问题是,是否合法来优化这一点,所以这并不能解决任何问题。
答案 0 :(得分:8)
JLS仅指定程序生成的可观察行为的合约。由于myInt
是本地的,因此优化确实可以在编译时进行优化,因为这会产生与规范一致的行为,并且规范中没有任何内容表明它不被允许(至少,不是我找到的!)。规范的Chapter 1明确指定了规范的可观察性:This document fully specifies the (apparent) order of evaluation of expressions...
。由于通过常量折叠到myInt = 43
,表观行为没有改变,因此优化将与JLS一致。
实际上,Java应用程序的编译目标甚至没有在JLS中指定。第1章说Java应用程序&#34;通常&#34;编译为JVM规范中指定的字节码(单独的文档),但不要求它们这样做。 <{3}} 必须在编译时进行优化,但myInt
不是这样的。即使myInt
是一个字段,我认为允许进行优化;即使myInt
是易变的(因为它代表一个有效的事件排序),不同的行为仍然是有效的行为。
所以,简短的回答,我认为你的学生是正确的;把它优化到myInt = 43
是完全没问题的。也就是说,javac
通常在优化方面做得很少 - 几乎没有。优化几乎全部在JIT中完成。
答案 1 :(得分:5)
我相信Java编译器可以在编译时静态地确定任何常量折叠。
是的,这个可以通过javac
进行优化。
但是javac
不需要进行此优化,因为JVM JIT编译器几乎肯定会为您进行相同的优化。从这个角度来看,无论是Java还是&gt;字节码编译器在对运行时执行的实际本机代码的影响方面可能无关紧要。
答案 2 :(得分:0)
所以这里没有优化。
考虑到Java使用动态编译,这不应该太令人惊讶。
JVM在运行时优化了几乎所有代码,这意味着无论您使用的是1996年为Java 1.0编译的代码还是Scala或JRuby,JGo等,您都可以充分利用特定的本机代码进行优化。 CPU型号。
由于这个原因,许多语言都有JVM实现,所以他们不需要为运行JVM的所有平台生成最佳代码。
答案 3 :(得分:0)
虽然这是真的,但我的问题是编译器是否合法 允许进行此优化
通过传统的数据流分析(流/传递函数,插入,出集,生成集,终止集等),特别是某些路径转发数据流,又名达到定义(或使用 - def链)......是的。实质上,优化器可以将变量的每次使用链接到达到它的定义。
在这种情况下,优化器可以确定myInt(= 137)的初始定义从未达到其使用,而另一个定义(= 42 1 )确实达到了它的用途,因为它永远不会在从该定义到其使用的任何(某些)路径上重新定义。
参考文献:
1 - 我在教学代码中一直使用42。我打赌我们都这样做。