Basic knowledge of
A diagram to understand the bytecode structure
Execution mode
- Interpretation execution: the interpreter will interpret our sentences into binary codes that can be recognized by the machine for execution, such as Dalvik
- Compile execution: the source program is translated into machine instructions by the compiler (responsible for translating the source program into target machine instructions), compiled –> link –> target executable, and then executed; That is to convert all source code into binary instructions at once in advance, that is, to generate an executable program, such as the old JIT, C++
- Both: the new JIT
Regardless of the execution mode, the one thing that is consistent is that bytecode is a stack-based execution engine, as the following examples illustrate. Let’s take a look at its data structure
Execution data structure
Stack frame: Elements in a stack
- Local variable table: Used to store local variables
- Operand stack: Used to execute functions and inputs
- Dynamic linking: Supports method reference conversion at run time
- Return address: Method return is supported
Basic instructions
astore_n
: The top reference value is stored in the NTH local variableinvokespecial
:invokevirtual
: normal method calldup
: copies the top value of the stack and pushes it to the topldc
: Pushes constants to the top of the stackaload_n
: Pushes the NTH variable of reference type to the top of the stack
The example analysis
Java source code
package jvm;
public class Language {
public int count = 1;
public String getContent(a) {
return "hello world";
}
public String getContent2(a) {
return "hello world 2"; }}public class Person {
private final String name = "person";
private final Language mLanguage = new Language();
public void say(a) {
System.out.println("Person say " + mLanguage.getContent());
}
public static void main(String[] args) {
Person person = newPerson(); person.say(); }}Copy the code
Javap -v Person output
public class jvm.Person
minor version: 0
major version: 52
flags: ACC_PUBLIC.ACC_SUPER
Constant pool# 1:= Methodref #18.#33 // java/lang/Object."<init>":()V
#2 = String #34 // person
#3 = Fieldref #15.#35 // jvm/Person.name:Ljava/lang/String;
#4 = Class #36 // jvm/Language
#5 = Methodref #4.#33 // jvm/Language."<init>":()V
#6 = Fieldref #15.#37 // jvm/Person.mLanguage:Ljvm/Language;
#7 = Fieldref #38.#39 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Class #40 // java/lang/StringBuilder
#9 = Methodref #8.#33 // java/lang/StringBuilder."<init>":()V
#10 = String #41 // Person say
#11 = Methodref #8.#42 // java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
#12 = Methodref #4.#43 // jvm/Language.getContent:()Ljava/lang/String;
#13 = Methodref #8.#44 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#14 = Methodref #45.#46 // java/io/PrintStream.println:(Ljava/lang/String;) V
#15 = Class #47 // jvm/Person
#16 = Methodref #15.#33 // jvm/Person."<init>":()V
#17 = Methodref #15.#48 // jvm/Person.say:()V
#18 = Class #49 // java/lang/Object
#19 = Utf8 name
#20 = Utf8 Ljava/lang/String;
#21 = Utf8 ConstantValue
#22 = Utf8 mLanguage
#23 = Utf8 Ljvm/Language;
#24 = Utf8 <init>
#25 = Utf8 ()V
#26 = Utf8 Code
#27 = Utf8 LineNumberTable
#28 = Utf8 say
#29 = Utf8 main
#30 = Utf8 ([Ljava/lang/String;)V
#31 = Utf8 SourceFile
#32 = Utf8 Person.java
#33 = NameAndType #24: #25 // "<init>":()V
#34 = Utf8 person
#35 = NameAndType #19: #20 // name:Ljava/lang/String;
#36 = Utf8 jvm/Language
#37 = NameAndType #22: #23 // mLanguage:Ljvm/Language;
#38 = Class #50 // java/lang/System
#39 = NameAndType #51: #52 // out:Ljava/io/PrintStream;
#40 = Utf8 java/lang/StringBuilder
#41 = Utf8 Person say
#42 = NameAndType #53: #54 // append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
#43 = NameAndType #55: #56 // getContent:()Ljava/lang/String;
#44 = NameAndType #57: #56 // toString:()Ljava/lang/String;
#45 = Class #58 // java/io/PrintStream
#46 = NameAndType #59: #60 // println:(Ljava/lang/String;) V
#47 = Utf8 jvm/Person
#48 = NameAndType #28: #25 // say:()V
#49 = Utf8 java/lang/Object
#50 = Utf8 java/lang/System
#51 = Utf8 out
#52 = Utf8 Ljava/io/PrintStream;
#53 = Utf8 append
#54= Utf8 (Ljava/lang/String;) Ljava/lang/StringBuilder; #55 = Utf8 getContent
#56 = Utf8 ()Ljava/lang/String;
#57 = Utf8 toString
#58 = Utf8 java/io/PrintStream
#59 = Utf8 println
#60= Utf8 (Ljava/lang/String;) V {private final java.lang.String name;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_FINAL
ConstantValue: String person
private final jvm.Language mLanguage;
descriptor: Ljvm/Language;
flags: ACC_PRIVATE, ACC_FINAL
public jvm.Person();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String person
7: putfield #3 // Field name:Ljava/lang/String;
10: aload_0
11: new #4 // class jvm/Language
14: dup
15: invokespecial #5 // Method jvm/Language."<init>":()V
18: putfield #6 // Field mLanguage:Ljvm/Language;
21: return
LineNumberTable:
line 3: 0
line 5: 4
line 6: 10
public void say(a);
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=1, args_size=1
// Get a static object
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
// The new three brothers appear again
3: new #8 // class java/lang/StringBuilder
6: dup
7: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
// Push constant #10 ('Person Say') in the constant pool to the top of the stack
10: ldc #10 // String Person say
// Call method stringBuilder.append ()
12: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
// Push this to the top of the stack in order to call getField
15: aload_0
// Fetch #6 (mLanguage) from the field set to the top of the stack
16: getfield #6 // Field mLanguage:Ljvm/Language;
#56 -> mlanguage.getContent (): String
19: invokevirtual #12 // Method jvm/Language.getContent:()Ljava/lang/String;
Builder.append (): StringBuilder In fact, the default English comments are already written clearly
22: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;) Ljava/lang/StringBuilder;
25: invokevirtual #13 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
28: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
31: return
LineNumberTable:
line 9: 0
line 10: 31
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
// New, DUP, invokespecial is a classical combination, which illustrates that new is not an atomic operation in nature
// Create an object reference (see constant pool table for object #15) and stack it
0: new #15 // class jvm/Person
// copy the object reference into the stack
3: dup
// Call the init method of Person (also visible here via the constant pool table)
4: invokespecial #16 // Method "<init>":()V
// store at the top of the stack (a reference to Person at this point) to the first local variable table, which is actually assigned to the local variable Person
7: astore_1
// Load the local variable table with index 1 at the top of the stack (index 0 is this), which is ready for the method call
8: aload_1
// Call the method specified in #17, where the method is say()
9: invokevirtual #17 // Method say:()V
12: return
LineNumberTable:
line 13: 0
line 14: 8
line 15: 12
}
SourceFile: "Person.java"
Copy the code