1. What is bytecode?
1. Bytecode overview
Java bytecode is the instruction set of the Java Virtual Machine. It acts like an assembler, which is an alias for C++ code. Once a java program is compiled, java bytecode is generated. In more appropriate terms, java bytecode is machine code in the form of a .class file. We have achieved platform independence in java with the help of java bytecode.
2. Bytecode and Assembly Language
Bytecode is similar to assembly language in that it is not a high-level language, but unlike machine language, it is still somewhat readable. Both can be thought of as an "intermediate language" between source code and machine code. The main difference between the two is that bytecode is generated for a virtual machine (software) whereas assembly language is generated for a CPU (hardware).
2. How does it work?
When we write a program in Java, first, the compiler compiles the program and generates a bytecode for the code. We can do this when we want to run this .class file on any other platform. After the first compilation, the resulting bytecode is now run by the Java virtual machine instead of the processor in question. This essentially means that we just need to install basic java on whatever platform we want to run the code on. The resources required to run the bytecode are provided by the Java virtual machine, which calls the processor to allocate the required resources. JVMs are stack based, so they stack implementations to read code.
Third, the advantages of Java bytecode
Platform independence is one of the important features that James Gosling gave to java, and it is the implementation of this bytecode that helps us achieve this. Therefore, bytecode is a very important part of any java program. The instruction set of the JVM may vary from system to system, but all can interpret bytecode. One thing to remember is that bytecode is non-runnable code and relies on the availability of an interpreter to execute, so the JVM comes into play.
Bytecode is essentially a machine-level language that runs on the Java Virtual Machine. Whenever a class is loaded, it gets a stream of bytecodes for each method of that class. Whenever the method is called during program execution, the method's bytecode is called. Javac not only compiles programs, but also generates bytecode for programs. Therefore, we have realized that the implementation of bytecode makes Java a platform independent language. This helps to add portability to Java that languages such as C or C++ lack. Portability ensures that Java can be implemented on various platforms such as desktop, mobile, server, etc. To support this, Sun Microsystems named JAVA "write once, read anywhere" or "WORA" to resonate with bytecode interpretation.
Fourth, how to view the bytecode
View through javac and javap: first compile the .java file into a .class bytecode file through javac, and then analyze the bytecode through javap -verbose.
1. javap command description
-help --help -? print this usage message
-version version info
-v -verbose print additional info
-l print line numbers and local variable table
-public only show public classes and members
-protected show protected/public classes and members
-package show package/protected/public classes and members (default)
-p -private show all classes and members
-c disassemble code
-s output internal type signatures
-sysinfo show system information for classes being processed ( path, size, date, MD5 hash)
-constants show final constants
-classpath <path> specify where to find user class files
-cp <path> specify where to find user class files
-bootclasspath <path> override where boot class files are located
2. Output additional information
javap -verbose Base
The output is as follows
Classfile /C:/Program Files/Java/jdk1.8.0_91/bin/Base.class
Last modified 2022-3-6; size 218 bytes
MD5 checksum 9a79a6c59f787265dae97730351d7e23
Compiled from "Base.java"
abstract class Base
minor version: 0
major version: 52
flags: ACC_SUPER, ACC_ABSTRACT
Constant pool:
#1 = Methodref #3.#12 // java/lang/Object."<init>":()V
#2 = Class #13 // Base
#3 = Class #14 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 method1
#9 = Utf8 method2
#10 = Utf8 SourceFile
#11 = Utf8 Base.java
#12 = NameAndType #4:#5 // "<init>":()V
#13 = Utf8 Base
#14 = Utf8 java/lang/Object
{
Base();
descriptor: ()V
flags:
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
public abstract void method1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_ABSTRACT
public abstract void method2();
descriptor: ()V
flags: ACC_PUBLIC, ACC_ABSTRACT
}
SourceFile: "Base.java"
3. Decompile the code
javap -c Base.class
The output is as follows
Compiled from "Base.java"
abstract class Base {
Base();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public abstract void method1();
public abstract void method2();
}
4. Idea configuration macro
Five, virtual machine bytecode instruction set
The image below is a partial example. See Java Virtual Machine Instruction Set for details
6. Simple example
1. Class code
package com.algorithm.demo.algorithms;
public class ByteCodeTest{
public static void InitAndAdd()
{
int i=99;
i++;
}
}
2. Bytecode
Compiled from "ByteCodeTest.java"
public class com.algorithm.demo.algorithms.ByteCodeTest {
public com.algorithm.demo.algorithms.ByteCodeTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void InitAndAdd();
Code:
0: bipush 99
2: istore_0
3: iinc 0, 1
6: return
}
Let's focus on the InitAndAdd method.
1. int i=99; corresponds to the following two steps
0: bipush 99 //Push a byte into the operand stack (its length will be filled with 4 bytes)
2: istore_0 //Store the int into a local variable
2. i++; corresponds to the following two steps
3: iinc 0, 1 //increment local variable
6: return //return