编译后的类文件在Java中保留了多少源代码信息?
当我自己的另一个项目的类文件被IntelliJ反编译时 (由Fernflower de compiler编写), 我对反编译代码与其他代码相比的接近性感到惊讶 源代码, 即使是方法局部变量名也与原始源代码相同 我不知道Java编译过程是如何工作的 JVM是如何工作的, 我天真的理解是,公众人物的名字 可能需要在编译后保存, 但是局部变量的名称, 它们只是帮助人类阅读的助记符, 在他们的范围之外完全没有用, 我认为JVM不需要这些信息 所以, 反编译器是否通过某种魔法简单地计算出了这些信息编译后的类文件在Java中保留了多少源代码信息?,java,bytecode,.class-file,Java,Bytecode,.class File,当我自己的另一个项目的类文件被IntelliJ反编译时 (由Fernflower de compiler编写), 我对反编译代码与其他代码相比的接近性感到惊讶 源代码, 即使是方法局部变量名也与原始源代码相同 我不知道Java编译过程是如何工作的 JVM是如何工作的, 我天真的理解是,公众人物的名字 可能需要在编译后保存, 但是局部变量的名称, 它们只是帮助人类阅读的助记符, 在他们的范围之外完全没有用, 我认为JVM不需要这些信息 所以, 反编译器是否通过某种魔法简单地计算出了这些信息 或者编
或者编译后的类是否保留了大量信息以及用于什么?最后,它取决于实际的编译器和确切的编译设置 正如您所指出的,JVM本身不需要任何局部变量名。(严格地说,它实际上也不需要方法名。甚至可以有,但我必须在规范中查找更多关于此的细节,以便更深入地说明)。但是类文件可以包含超出JVM所需信息范围的额外调试信息 标准Java编译器是。文档中已经包含了一些关于可能的调试信息的提示: -g 生成所有调试信息,包括局部变量。默认情况下,只有 生成行号和源文件信息 -g:none 不会生成任何调试信息 -g:[关键字列表] 仅生成某些类型的调试信息,由逗号分隔的关键字列表指定。有效关键字包括:
- 源:源文件调试信息
- 行:行号调试信息
- vars:局部变量调试信息
public class ExampleClass {
public static void main(String[] args) {
ExampleClass exampleClass = new ExampleClass();
exampleClass.exampleMethod();
}
public void exampleMethod() {
String string = "This is an example";
for (int counter = 0; counter < 10; counter++) {
String localResult = string + counter;
System.out.println(localResult);
}
}
}
将生成一个类文件。使用打印有关此类文件的信息
javap -c -v -l ExampleClass.class
(其中-c
表示反汇编输出,-v
表示输出应详细,-l
表示应打印行号信息),输出如下:
public class ExampleClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#22 // java/lang/Object."<init>":()V
#2 = Class #23 // ExampleClass
#3 = Methodref #2.#22 // ExampleClass."<init>":()V
#4 = Methodref #2.#24 // ExampleClass.exampleMethod:()V
#5 = String #25 // This is an example
#6 = Class #26 // java/lang/StringBuilder
#7 = Methodref #6.#22 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#27 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = Methodref #6.#28 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#10 = Methodref #6.#29 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #30.#31 // java/lang/System.out:Ljava/io/PrintStream;
#12 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #34 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 exampleMethod
#20 = Utf8 StackMapTable
#21 = Class #35 // java/lang/String
#22 = NameAndType #14:#15 // "<init>":()V
#23 = Utf8 ExampleClass
#24 = NameAndType #19:#15 // exampleMethod:()V
#25 = Utf8 This is an example
#26 = Utf8 java/lang/StringBuilder
#27 = NameAndType #36:#37 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#28 = NameAndType #36:#38 // append:(I)Ljava/lang/StringBuilder;
#29 = NameAndType #39:#40 // toString:()Ljava/lang/String;
#30 = Class #41 // java/lang/System
#31 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
#32 = Class #44 // java/io/PrintStream
#33 = NameAndType #45:#46 // println:(Ljava/lang/String;)V
#34 = Utf8 java/lang/Object
#35 = Utf8 java/lang/String
#36 = Utf8 append
#37 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#38 = Utf8 (I)Ljava/lang/StringBuilder;
#39 = Utf8 toString
#40 = Utf8 ()Ljava/lang/String;
#41 = Utf8 java/lang/System
#42 = Utf8 out
#43 = Utf8 Ljava/io/PrintStream;
#44 = Utf8 java/io/PrintStream
#45 = Utf8 println
#46 = Utf8 (Ljava/lang/String;)V
{
public ExampleClass();
descriptor: ()V
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
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: new #2 // class ExampleClass
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method exampleMethod:()V
12: return
public void exampleMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #5 // String This is an example
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 43
11: new #6 // class java/lang/StringBuilder
14: dup
15: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_3
30: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_3
34: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 2, 1
40: goto 5
43: return
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 37
}
保留所有调试信息。如上所述打印结果产生
public class ExampleClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#36 // java/lang/Object."<init>":()V
#2 = Class #37 // ExampleClass
#3 = Methodref #2.#36 // ExampleClass."<init>":()V
#4 = Methodref #2.#38 // ExampleClass.exampleMethod:()V
#5 = String #39 // This is an example
#6 = Class #40 // java/lang/StringBuilder
#7 = Methodref #6.#36 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#41 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = Methodref #6.#42 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#10 = Methodref #6.#43 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #44.#45 // java/lang/System.out:Ljava/io/PrintStream;
#12 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #48 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 LExampleClass;
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 args
#24 = Utf8 [Ljava/lang/String;
#25 = Utf8 exampleClass
#26 = Utf8 exampleMethod
#27 = Utf8 localResult
#28 = Utf8 Ljava/lang/String;
#29 = Utf8 counter
#30 = Utf8 I
#31 = Utf8 string
#32 = Utf8 StackMapTable
#33 = Class #49 // java/lang/String
#34 = Utf8 SourceFile
#35 = Utf8 ExampleClass.java
#36 = NameAndType #14:#15 // "<init>":()V
#37 = Utf8 ExampleClass
#38 = NameAndType #26:#15 // exampleMethod:()V
#39 = Utf8 This is an example
#40 = Utf8 java/lang/StringBuilder
#41 = NameAndType #50:#51 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#42 = NameAndType #50:#52 // append:(I)Ljava/lang/StringBuilder;
#43 = NameAndType #53:#54 // toString:()Ljava/lang/String;
#44 = Class #55 // java/lang/System
#45 = NameAndType #56:#57 // out:Ljava/io/PrintStream;
#46 = Class #58 // java/io/PrintStream
#47 = NameAndType #59:#60 // println:(Ljava/lang/String;)V
#48 = Utf8 java/lang/Object
#49 = Utf8 java/lang/String
#50 = Utf8 append
#51 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#52 = Utf8 (I)Ljava/lang/StringBuilder;
#53 = Utf8 toString
#54 = Utf8 ()Ljava/lang/String;
#55 = Utf8 java/lang/System
#56 = Utf8 out
#57 = Utf8 Ljava/io/PrintStream;
#58 = Utf8 java/io/PrintStream
#59 = Utf8 println
#60 = Utf8 (Ljava/lang/String;)V
{
public ExampleClass();
descriptor: ()V
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 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LExampleClass;
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: new #2 // class ExampleClass
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method exampleMethod:()V
12: return
LineNumberTable:
line 4: 0
line 5: 8
line 6: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 exampleClass LExampleClass;
public void exampleMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #5 // String This is an example
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 43
11: new #6 // class java/lang/StringBuilder
14: dup
15: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_3
30: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_3
34: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 2, 1
40: goto 5
43: return
LineNumberTable:
line 9: 0
line 10: 3
line 11: 11
line 12: 30
line 10: 37
line 14: 43
LocalVariableTable:
Start Length Slot Name Signature
30 7 3 localResult Ljava/lang/String;
5 38 2 counter I
0 44 0 this LExampleClass;
3 41 1 string Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 37
}
SourceFile: "ExampleClass.java"
和的文档中给出了有关这些属性的结构的详细信息
对于行号表
,它会显示
调试器可以使用它来确定代码数组的哪个部分对应于原始源文件中的给定行号
对于LocalVariableTable
,它说
调试器可以使用它在方法执行期间确定给定局部变量的值
在javap
的输出中,已经解析了局部变量的名称。但是,表本身中包含的实际信息只是常量池中的一个索引(这就是为什么保留调试信息时会有更多的条目)。例如,localResult
变量的条目显示为
30 7 3 localResult Ljava/lang/String;
虽然它实际上只包含对条目的引用
#27 = Utf8 localResult
在恒定池中
那么,这些信息是由反编译器通过某种魔法简单地计算出来的,还是编译后的类保留了大量信息,这是为了什么
如上所示,编译后的类可以保留大量信息。毕竟,IDE的主要用途之一是为调试器提供一个漂亮的可视化界面。因此,大多数由IDE以某种方式触发的编译器在默认情况下都会尽可能多地保留调试信息。我确信没有“魔法”参与,所有使反编译器能够生成令人惊讶的原始代码复制品所需的信息都包含在类文件中。的确,JVM不需要局部变量的名称,但它们可能对其他工具(如调试器和分析器)很有用。另一方面,对于更复杂的代码,反编译器的效果不太好。如果字节码被混淆了,它可能会做得不好。(至少,这是进行模糊处理的人想要的。)另外需要记住的是,javac的命令行选项控制有多少信息进入类文件。(请参见中的“-g”选项)。即使没有调试信息,也会包含很多名称(方法等),因为必须允许反射工作。如果使用maven构建项目,我应该如何配置maven以不生成任何调试信息@Marco13@Yulin我还必须在那里查找详细信息,但我认为插入
-g:none
作为一个,应该已经完成了这项工作……这是可行的,现在所有局部变量在反编译代码中都被命名为var@Marco13还有-parameters
选项,它可以存储调试属性之外的参数名。
LineNumberTable:
line 9: 0
line 10: 3
line 11: 11
line 12: 30
line 10: 37
line 14: 43
LocalVariableTable:
Start Length Slot Name Signature
30 7 3 localResult Ljava/lang/String;
5 38 2 counter I
0 44 0 this LExampleClass;
3 41 1 string Ljava/lang/String;
30 7 3 localResult Ljava/lang/String;
#27 = Utf8 localResult