我在阅读Java字节码时看到了这个:
getfield #5 (Field java.lang.String name)
#5
是什么意思?
我如何使用字节码编写程序?
Java类文件和字节码
Java类文件(字节码文件)由不同的组件组成:
http://en.wikipedia.org/wiki/Java_class_file
编号#5只是指向常量池中的一个位置。在该位置上找到了一个包含对常量名和类型的引用的CONSTANT_FieldRef,以及其他属性。CONSTANT_NameAndType包含对CONSTANT_Utf8(其中包含实际的字符串/名称)的引用。
所以流程如下:
getfield #number -> FieldRef -> NameAndType -> Utf8 -> string
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
因此,不是在每个getfield
指令中保存整个字符串,而是保存一个数字。这提高了解释器(或JIT)的性能和类文件中的空间利用率。
手写字节码
可以使用此工具(其中包含许多示例)将手写字节码组装到类文件中:
getfield
指令(如我所记)会引用类文件中的常量池,以获取有关应查找哪个字段的信息。这里的#5表示“常量池条目号5”,而该常量池则包含有关“查找类型为java.lang.String
的name
字段”的信息。原因在于,它使得getfield
指令的大小保持不变,无论要查找的字段的名称或类型是什么。System.out.println("hello world");
bytecode
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello world
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
字节码指令的格式为指令 常量池索引
Constant pool:
#4 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
#28 = Class #36 // java/io/PrintStream
#29 = NameAndType #37:#38 // println:(Ljava/lang/String;)V
#36 = Utf8 java/io/PrintStream
#37 = Utf8 println
#38 = Utf8 (Ljava/lang/String;)V
因此,这个指令将调用索引为4的方法。
invokevirtual #4
而索引4是一个Methodref
,它属于// java/io/PrintStream.println:(Ljava/lang/String;)V
如果我们遵循常量池中的引用,我们会发现所有信息都存储在字符串中,并组成像方法、字段、类等复杂类型。