为什么javac创建了一个未使用的带有后缀的变量++&引用;接线员在上课吗?
受此启发,我开始了一项小小的研究 我能够确定使用原语时,编译器会将前缀为什么javac创建了一个未使用的带有后缀的变量++&引用;接线员在上课吗?,java,compiler-optimization,Java,Compiler Optimization,受此启发,我开始了一项小小的研究 我能够确定使用原语时,编译器会将前缀++I重写为I++: 之前: public class PrefixIncrement { public static void main(String args[]) { for(Integer i = 0; i < 100; ++i) { System.out.println(i); } } } public class PostfixI
++I
重写为I++
:
之前:
public class PrefixIncrement {
public static void main(String args[]) {
for(Integer i = 0; i < 100; ++i) {
System.out.println(i);
}
}
}
public class PostfixIncrement {
public static void main(String args[]) {
for( Integer i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
============================按请求:javap输出=======================
使用Integer
类的后缀增量
{
public com.foo.PostfixIncrement();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/matt/PostfixIncrement;
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_0
1: invokestatic #2 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
4: astore_1
5: aload_1
6: invokevirtual #3 // Method java/lang/Integer.intVal
ue:()I
9: bipush 100
11: if_icmpge 40
14: getstatic #4 // Field java/lang/System.out:Ljav
a/io/PrintStream;
17: aload_1
18: invokevirtual #5 // Method java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
21: aload_1
22: astore_2
23: aload_1
24: invokevirtual #3 // Method java/lang/Integer.intVal
ue:()I
27: iconst_1
28: iadd
29: invokestatic #2 // Method java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
32: dup
33: astore_1
34: astore_3
35: aload_2
36: pop
37: goto 5
40: return
LineNumberTable:
line 5: 0
line 6: 14
line 5: 21
line 8: 40
LocalVariableTable:
Start Length Slot Name Signature
5 35 1 i Ljava/lang/Integer;
0 41 0 args [Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 5
locals = [ class java/lang/Integer ]
frame_type = 250 /* chop */
offset_delta = 34
}
{
public com.foo.postfix增量();
旗帜:ACC_PUBLIC
代码:
堆栈=1,局部变量=1,参数大小=1
0:aload_0
1:invokespecial#1//方法java/lang/Object。“
“:()V
4:返回
LineNumberTable:
第3行:0
LocalVariableTable:
起始长度插槽名称签名
0 5 0此Lcom/matt/POSTFIX增量;
公共静态void main(java.lang.String[]);
标志:ACC_公共,ACC_静态
代码:
堆栈=2,局部变量=4,参数大小=1
0:iconst_0
1:invokestatic#2//方法java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
4:astore_1
5:aload_1
6:invokevirtual#3//方法java/lang/Integer.intVal
ue:()我
9:bipush 100
11:if_icmpge 40
14:getstatic#4//Field java/lang/System.out:Ljav
a/io/PrintStream;
17:aload_1
18:invokevirtual#5//方法java/io/PrintStream.prin
tln:(Ljava/lang/Object;)V
21:aload_1
22:astore_2
23:aload_1
24:invokevirtual#3//方法java/lang/Integer.intVal
ue:()我
27:iconst_1
28:iadd
29:invokestatic#2//方法java/lang/Integer.valueO
f:(I)Ljava/lang/Integer;
32:dup
33:astore_1
34:astore_3
35:aload_2
36:流行音乐
37:goto 5
40:返回
LineNumberTable:
第5行:0
第6行:14
第5行:21
第8行:40
LocalVariableTable:
起始长度插槽名称签名
5351i Ljava/lang/Integer;
0 41 0 args[Ljava/lang/String;
StackMapTable:条目数=2
框架类型=252/*追加*/
偏移量δ=5
局部变量=[类java/lang/Integer]
框架类型=250/*印章*/
偏移量δ=34
}
前缀++的int版本的语义是:++i
更简单:i=i+1;返回i
而i++
意味着更复杂的事情:inti$=i;i=i+1;返回i$
当int被包装成整数时,使用的编译器显然不能生成足够智能的代码。它实际上保持了i
(上面的i$
)的旧值。没有理由如此愚蠢,因为i++
没有在该位置使用
for (...; ...; (void)[[ i++ ]] ) {
在第一个实例中生成
Integer i$ = i;
i = Integer.valueOf(i.intValue() + 1);
"return" i$ to a void context
drop variable i$ where i$ is unused
本应简化为:
i = Integer.valueOf(i.intValue() + 1);
我怀疑这两个临时变量,localInteger1
和localInteger2
是用来处理一般装箱/拆箱规则的占位符。不看反编译的字节码,它可能会揭示一些未从编译的java代码中重新解释的细节,也不看其他一些不太重要的情况,我只是在猜测
但是,很明显,它们在循环ForUpdate
表达式之前和之后都保留了i
的值
考虑这种情况:
Integer i;
for( i = 0; i < 100; i++) {
System.out.println(i);
}
System.out.println(i); // what is the value of `i` here?
整数i;
对于(i=0;i<100;i++){
系统输出打印LN(i);
}
System.out.println(i);//这里'i'的值是多少?
在Java代码中,除了声明的类型是Integer
而不是int
之外,它与原语的情况相同
编译器必须在循环执行期间和之后跟踪i
的值,此时它将是100
。我怀疑在实现循环和处理原语int
时会出现一些情况,但处理对象引用更新包括更复杂的情况,因此需要他有两个临时值。正如您在类的javap
输出(使用javac
1.8.0\u 60编译)中看到的,变量实际上并不存在:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: aload_1
6: invokevirtual #3 // Method java/lang/Integer.intValue:()I
9: bipush 100
11: if_icmpge 34
14: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_1
18: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
21: aload_1
22: invokevirtual #3 // Method java/lang/Integer.intValue:()I
25: iconst_1
26: iadd
27: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
30: astore_1
31: goto 5
34: return
(局部变量与*加载
和*存储
指令一起使用,如您所见,只涉及一个局部变量。)
这个问题似乎是由反编译器引起的,它可能不太支持在中对语句进行装箱。因此javac
没有任何错误,这只是反编译器的错误。不必担心性能影响或任何事,除非您计划进行反向工程并保存反编译的代码:p.什么原始代码是否具有Integer
类?这才是重要的,您没有在此处显示。您是否查看了生成的字节码?反编译不一定是实际源代码的准确表示。@Jeroenvanevel-->我用“Integer”版本更新了它。短版本只是将“int”替换为for循环声明中的“整数”。算术运算不依赖自动装箱。请改用内置类型(int
)。依赖自动装箱将导致隐藏的性能问题,如您所看到的问题。您还应该发布javap
输出。有时反编译器会插入疯狂的局部变量,而这些变量在遇到堆栈操作(如DUP
)时并不存在,它们的语义在Java中无法表达。您依赖反编译器输出以推断int case++i正在变成i++;这并不是真正发生的事情——当javac编译它们时,它将发出多个字节码;有些与处理v
i = Integer.valueOf(i.intValue() + 1);
Integer i;
for( i = 0; i < 100; i++) {
System.out.println(i);
}
System.out.println(i); // what is the value of `i` here?
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: aload_1
6: invokevirtual #3 // Method java/lang/Integer.intValue:()I
9: bipush 100
11: if_icmpge 34
14: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_1
18: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
21: aload_1
22: invokevirtual #3 // Method java/lang/Integer.intValue:()I
25: iconst_1
26: iadd
27: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
30: astore_1
31: goto 5
34: return