Skip to content

2019 05 08 用Java实现JVM第七章《方法调用和返回》

fuzhengwei edited this page May 16, 2020 · 1 revision

作者:小傅哥
博客:https://bugstack.cn - 原创系列专题

沉淀、分享、成长,让自己和他人都能有所收获!

案例介绍

本章节主要用java实现;方法调用指令、返回指令、解析方法符号引用、参数传递等。实现新的指令后我们的虚拟机就可以执行稍微复杂的运算并输出结果。

从调用的角度来看,方法可以分为两类:静态方法(或者类方法)和实例方法。静态方法通过类类调用,实例方法则通过对象引用来调用。静态方法是静态绑定的,也就是说,最终调用的是哪给方法在编译期就已经确定。实例方法则支持动态绑定,最终要调用哪给方法可能要推迟到运行期才能知道。

从实现的角度来看,方法可以分为三类:没有实现(也就是抽象方法)、用Java语言(或者JVM上其他的语言,如Groovy和Scala等)实现和用本地语言(如C或者C++)实现。静态方法和抽象方法是互斥的。在Java 8之前,接口只能包括抽象方法。为了实现Lambda表达式,Java 8放宽了这一限制,在接口中也可以定义静态方法和默认方法。

在Java 7之前,Java虚拟机规范一共提供了4条方法调用指令。其中invokestatic指令用来调用静态方法。invokespecial指令用来调用无须动态绑定的实例方法,包括构造函数、私有方法和通过super关键字调用的超类方法。剩下的情况则属于动态绑定。如果是针对接口类型的引用调用方法,就使用invokeinterface指令,否则使用invokevirtual指令。

环境准备

  1. jdk 1.8.0
  2. IntelliJ IDEA Community Edition 2018.3.1 x64

配置信息

  1. 调试配置
    1. 配置位置:Run/Debug Configurations -> program arguments
    2. 配置内容(选配 verbose true):-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-07\target\test-classes\org\itstack\demo\test\HelloWorld verbose true

代码示例

itstack-demo-jvm-07
├── pom.xml
└── src
    └── main
    │    └── java
    │        └── org.itstack.demo.jvm
    │             ├── classfile
    │             │   ├── attributes   {BootstrapMethods/Code/ConstantValue...}
    │             │   ├── constantpool {CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...}
    │             │   ├── ClassFile.java
    │             │   ├── ClassReader.java
    │             │   └── MemberInfo.java   
    │             ├── classpath
    │             │   ├── impl
    │             │   │   ├── CompositeEntry.java
    │             │   │   ├── DirEntry.java 
    │             │   │   ├── WildcardEntry.java 
    │             │   │   └── ZipEntry.java    
    │             │   ├── Classpath.java
    │             │   └── Entry.java   
    │             ├── classpath
    │             │   ├── base
    │             │   │   ├── BytecodeReader.java
    │             │   │   ├── ClassInitLogic.java
    │             │   │   ├── Instruction.java
    │             │   │   ├── InstructionBranch.java
    │             │   │   ├── InstructionIndex8.java
    │             │   │   ├── InstructionIndex16.java
    │             │   │   ├── InstructionNoOperands.java	
    │             │   │   └── MethodInvokeLogic.java
    │             │   ├── comparisons
    │             │   ├── constants
    │             │   ├── control
    │             │   ├── conversions
    │             │   ├── extended
    │             │   ├── loads
    │             │   ├── math
    │             │   ├── references
    │             │   │   ├── CHECK_CAST.java
    │             │   │   ├── GET_FIELD.java
    │             │   │   ├── GET_STATIC.java
    │             │   │   ├── INSTANCE_OF.java
    │             │   │   ├── INVOKE_INTERFACE.java
    │             │   │   ├── INVOKE_SPECIAL.java
    │             │   │   ├── INVOKE_STATIC.java
    │             │   │   ├── INVOKE_VIRTUAL.java
    │             │   │   ├── NEW.java
    │             │   │   ├── PUT_FIELD.java
    │             │   │   └── PUT_STATIC.java
    │             │   ├── stack
    │             │   ├── store
    │             │   └── Factory   
    │             ├── rtda
    │             │   ├── heap
    │             │   │   ├── constantpool
    │             │   │   ├── methodarea
    │             │   │   │   ├── Class.java    
    │             │   │   │   ├── ClassMember.java  
    │             │   │   │   ├── Field.java    
    │             │   │   │   ├── Method.java 
    │             │   │   │   ├── MethodDescriptor.java 
    │             │   │   │   ├── MethodDescriptorParser.java 
    │             │   │   │   ├── MethodLookup.java 	
    │             │   │   │   ├── Object.java   
    │             │   │   │   └── Slots.java        
    │             │   │   └── ClassLoader.java  
    │             │   ├── Frame.java
    │             │   ├── JvmStack.java
    │             │   ├── LocalVars.java
    │             │   ├── OperandStack.java
    │             │   ├── Slot.java 
    │             │   └── Thread.java
    │             ├── Cmd.java
    │             ├── Interpret.java    
    │             └── Main.java
    └── test
         └── java
             └── org.itstack.demo.test
                 └── HelloWorld.java

代码篇幅较长,只列重点代码块

MethodInvokeLogic.java

package org.itstack.demo.jvm.instructions.base;

import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.Slot;
import org.itstack.demo.jvm.rtda.Thread;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;

/**
 * http://www.itstack.org
 * create by fuzhengwei on 2019/4/28
 */
public class MethodInvokeLogic {

    public static void invokeMethod(Frame invokerFrame, Method method) {
        Thread thread = invokerFrame.thread();
        Frame newFrame = thread.newFrame(method);
        thread.pushFrame(newFrame);

        int argSlotCount = method.argSlotCount();
        if (argSlotCount > 0) {
            for (int i = argSlotCount - 1; i >= 0; i--) {
                Slot slot = invokerFrame.operandStack().popSlot();
                newFrame.localVars().setSlot(i, slot);
            }
        }

        //hack
        if (method.isNative()) {
            if ("registerNatives".equals(method.name())) {
                thread.popFrame();
            } else {
                throw new RuntimeException("native method " + method.name());
            }
        }
    }

}

INVOKE_INTERFACE.java

package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.BytecodeReader;
import org.itstack.demo.jvm.instructions.base.Instruction;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.constantpool.InterfaceMethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
import org.itstack.demo.jvm.rtda.heap.methodarea.MethodLookup;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

/**
 * http://www.itstack.org
 * create by fuzhengwei on 2019/4/28
 */
public class INVOKE_INTERFACE implements Instruction {

    private int idx;

    @Override
    public void fetchOperands(BytecodeReader reader) {
        this.idx = reader.readShort();
        reader.readByte();
        reader.readByte();
    }

    @Override
    public void execute(Frame frame) {
        RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();
        InterfaceMethodRef methodRef = (InterfaceMethodRef) runTimeConstantPool.getConstants(this.idx);
        Method resolvedMethod = methodRef.resolvedInterfaceMethod();
        if (resolvedMethod.isStatic() || resolvedMethod.isPrivate()) {
            throw new IncompatibleClassChangeError();
        }
        Object ref = frame.operandStack().getRefFromTop(resolvedMethod.argSlotCount() - 1);
        if (null == ref) {
            throw new NullPointerException();
        }
        if (!ref.clazz().isImplements(methodRef.resolvedClass())) {
            throw new IncompatibleClassChangeError();
        }
        Method methodToBeInvoked = MethodLookup.lookupMethodInClass(ref.clazz(), methodRef.name(), methodRef.descriptor());
        if (null == methodToBeInvoked || methodToBeInvoked.isAbstract()) {
            throw new AbstractMethodError();
        }
        if (!methodToBeInvoked.isPublic()) {
            throw new IllegalAccessError();
        }

        MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);

    }

}

INVOKE_SPECIAL.java

package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.InstructionIndex16;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.constantpool.MethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
import org.itstack.demo.jvm.rtda.heap.methodarea.MethodLookup;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

public class INVOKE_SPECIAL extends InstructionIndex16 {

    @Override
    public void execute(Frame frame) {
        Class currentClass = frame.method().clazz();
        RunTimeConstantPool runTimeConstantPool = currentClass.constantPool();
        MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
        Class resolvedClass = methodRef.resolvedClass();
        Method resolvedMethod = methodRef.ResolvedMethod();
        if ("<init>".equals(resolvedMethod.name()) && resolvedMethod.clazz() != resolvedClass) {
            throw new NoSuchMethodError();
        }
        if (resolvedMethod.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        Object ref = frame.operandStack().getRefFromTop(resolvedMethod.argSlotCount() - 1);
        if (null == ref) {
            throw new NullPointerException();
        }

        if (resolvedMethod.isProtected() &&
                resolvedMethod.clazz().isSubClassOf(currentClass) &&
                !resolvedMethod.clazz().getPackageName().equals(currentClass.getPackageName()) &&
                ref.clazz() != currentClass &&
                !ref.clazz().isSubClassOf(currentClass)) {
            throw new IllegalAccessError();
        }

        Method methodToBeInvoked = resolvedMethod;
        if (currentClass.isSuper() &&
                resolvedClass.isSubClassOf(currentClass) &&
                !resolvedMethod.name().equals("<init>")) {
            MethodLookup.lookupMethodInClass(currentClass.superClass, methodRef.name(), methodRef.descriptor());
        }

        if (methodToBeInvoked.isAbstract()) {
            throw new AbstractMethodError();
        }

        MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);

    }

}

INVOKE_STATIC.java

package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.ClassInitLogic;
import org.itstack.demo.jvm.instructions.base.InstructionIndex16;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.heap.constantpool.MethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;

/**
 * http://www.itstack.org
 * create by fuzhengwei on 2019/4/28
 */
public class INVOKE_STATIC extends InstructionIndex16 {

    @Override
    public void execute(Frame frame) {
        RunTimeConstantPool runTimeConstantPool = frame.method().clazz().constantPool();
        MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
        Method resolvedMethod = methodRef.ResolvedMethod();

        if (!resolvedMethod.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        Class clazz = resolvedMethod.clazz();
        if (!clazz.initStarted()) {
            frame.revertNextPC();
            ClassInitLogic.initClass(frame.thread(), clazz);
            return;
        }

        MethodInvokeLogic.invokeMethod(frame, resolvedMethod);
    }
}

INVOKE_VIRTUAL.java

package org.itstack.demo.jvm.instructions.references;

import org.itstack.demo.jvm.instructions.base.InstructionIndex16;
import org.itstack.demo.jvm.instructions.base.MethodInvokeLogic;
import org.itstack.demo.jvm.rtda.Frame;
import org.itstack.demo.jvm.rtda.OperandStack;
import org.itstack.demo.jvm.rtda.heap.constantpool.MethodRef;
import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;
import org.itstack.demo.jvm.rtda.heap.methodarea.Class;
import org.itstack.demo.jvm.rtda.heap.methodarea.Method;
import org.itstack.demo.jvm.rtda.heap.methodarea.MethodLookup;
import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

public class INVOKE_VIRTUAL extends InstructionIndex16 {

    @Override
    public void execute(Frame frame) {

        Class currentClass = frame.method().clazz();
        RunTimeConstantPool runTimeConstantPool = currentClass.constantPool();
        MethodRef methodRef = (MethodRef) runTimeConstantPool.getConstants(this.idx);
        Method resolvedMethod = methodRef.ResolvedMethod();
        if (resolvedMethod.isStatic()) {
            throw new IncompatibleClassChangeError();
        }

        Object ref = frame.operandStack().getRefFromTop(resolvedMethod.argSlotCount() - 1);
        if (null == ref) {
            if ("println".equals(methodRef.name())) {
                _println(frame.operandStack(), methodRef.descriptor());
                return;
            }
            throw new NullPointerException();
        }

        if (resolvedMethod.isProtected() &&
                resolvedMethod.clazz().isSubClassOf(currentClass) &&
                !resolvedMethod.clazz().getPackageName().equals(currentClass.getPackageName()) &&
                ref.clazz() != currentClass &&
                !ref.clazz().isSubClassOf(currentClass)) {
            throw new IllegalAccessError();
        }

        Method methodToBeInvoked = MethodLookup.lookupMethodInClass(ref.clazz(), methodRef.name(), methodRef.descriptor());
        if (null == methodToBeInvoked || methodToBeInvoked.isAbstract()) {
            throw new AbstractMethodError();
        }

        MethodInvokeLogic.invokeMethod(frame, methodToBeInvoked);
    }

    //hack
    private void _println(OperandStack stack, String descriptor) {
        switch (descriptor) {
            case "(Z)V":
                System.out.println(stack.popInt() != 0);
                break;
            case "(C)V":
                System.out.println(stack.popInt());
                break;
            case "(I)V":
            case "(B)V":
            case "(S)V":
                System.out.println(stack.popInt());
                break;
            case "(F)V":
                System.out.println(stack.popFloat());
                break;
            case "(J)V":
                System.out.println(stack.popLong());
                break;
            case "(D)V":
                System.out.println(stack.popDouble());
                break;
            default:
                System.out.println(descriptor);
                break;
        }
        stack.popRef();
    }
}

MethodDescriptor.java

package org.itstack.demo.jvm.rtda.heap.methodarea;

import java.util.ArrayList;
import java.util.List;

/**
 * http://www.itstack.org
 * create by fuzhengwei on 2019/4/28
 */
public class MethodDescriptor {

    public List<String> parameterTypes = new ArrayList<>();
    public String returnType;

    public void addParameterType(String type){
        this.parameterTypes.add(type);
    }

}

MethodDescriptorParser.java

package org.itstack.demo.jvm.rtda.heap.methodarea;

import javax.management.ObjectName;

/**
 * http://www.itstack.org
 * create by fuzhengwei on 2019/4/28
 */
public class MethodDescriptorParser {

    private String raw;
    private int offset;
    private MethodDescriptor parsed;

    public static MethodDescriptor parseMethodDescriptorParser(String descriptor) {
        MethodDescriptorParser parser = new MethodDescriptorParser();
        return parser.parse(descriptor);
    }

    public MethodDescriptor parse(String descriptor) {
        this.raw = descriptor;
        this.parsed = new MethodDescriptor();
        this.startParams();
        this.parseParamTypes();
        this.endParams();
        this.parseReturnType();
        this.finish();
        return this.parsed;
    }

    private void startParams() {
        if (this.readUint8() != '(') {
            causePanic();
        }
    }

    private void endParams() {
        if (this.readUint8() != ')') {
            causePanic();
        }
    }

    public void finish(){
        if (this.offset != this.raw.length()){
            this.causePanic();
        }
    }

    public void causePanic() {
        throw new RuntimeException("BAD descriptor:" + this.raw);
    }

    public byte readUint8() {
        byte[] bytes = this.raw.getBytes();
        byte b = bytes[this.offset];
        this.offset++;
        return b;
    }

    public void unreadUint8() {
        this.offset--;
    }

    public void parseParamTypes() {
        while (true) {
            String type = this.parseFieldType();
            if ("".equals(type)) break;
            this.parsed.addParameterType(type);
        }
    }

    public void parseReturnType() {
        if (this.readUint8() == 'V'){
            this.parsed.returnType = "V";
            return;
        }

        this.unreadUint8();
        String type = this.parseFieldType();
        if (!"".equals(type)){
            this.parsed.returnType = type;
            return;
        }

        this.causePanic();
    }

    public String parseFieldType() {
        switch (this.readUint8()) {
            case 'B':
                return "B";
            case 'C':
                return "C";
            case 'D':
                return "D";
            case 'F':
                return "F";
            case 'I':
                return "I";
            case 'J':
                return "J";
            case 'S':
                return "S";
            case 'Z':
                return "Z";
            case 'L':
                return this.parseObjectType();
            case '[':
                return this.parseArrayType();
            default:
                this.unreadUint8();
                return "";
        }
    }

    private String parseObjectType() {
        String unread = this.raw.substring(this.offset);
        int semicolonIndx = unread.indexOf(";");
        if (semicolonIndx == -1) {
            this.causePanic();
            return "";
        }
        int objStart = this.offset - 1;
        int ojbEnd = this.offset + semicolonIndx + 1;
        this.offset = ojbEnd;
        //descriptor
        return this.raw.substring(objStart, ojbEnd);
    }

    private String parseArrayType() {
        int arrStart = this.offset - 1;
        this.parseFieldType();
        int arrEnd = this.offset;
        //descriptor
        return this.raw.substring(arrStart, arrEnd);
    }


}

MethodLookup.java

package org.itstack.demo.jvm.rtda.heap.methodarea;

/**
 * http://www.itstack.org
 * create by fuzhengwei on 2019/4/28
 */
public class MethodLookup {

    static public Method lookupMethodInClass(Class clazz, String name, String descriptor) {
        for (Class c = clazz; c != null; c = c.superClass) {
            for (Method method : c.methods) {
                if (method.name.equals(name) && method.descriptor.equals(descriptor)) {
                    return method;
                }
            }
        }
        return null;
    }

    static public Method lookupMethodInInterfaces(Class[] ifaces, String name, String descriptor) {
        for (Class inface : ifaces) {
            for (Method method : inface.methods) {
                if (method.name.equals(name) && method.descriptor.equals(descriptor)) {
                    return method;
                }
            }
        }
        return null;
    }

}

Interpret.java

//指令集解释器
class Interpret {

    Interpret(Method method, boolean logInst) {
        Thread thread = new Thread();
        Frame frame = thread.newFrame(method);
        thread.pushFrame(frame);

        loop(thread, logInst);
    }

    private void loop(Thread thread, boolean logInst) {
        BytecodeReader reader = new BytecodeReader();
        while (true) {
            Frame frame = thread.currentFrame();
            int pc = frame.nextPC();
            thread.setPC(pc);

            reader.reset(frame.method().code, pc);
            byte opcode = reader.readByte();
            Instruction inst = Factory.newInstruction(opcode);
            if (null == inst) {
                System.out.println("Unsupported opcode " + byteToHexString(new byte[]{opcode}));
                break;
            }
            inst.fetchOperands(reader);
            frame.setNextPC(reader.pc());

            if (logInst) {
                logInstruction(frame, inst, opcode);
            }

            //exec
            inst.execute(frame);

            if (thread.isStackEmpty()) {
                break;
            }
        }
    }

    private static void logInstruction(Frame frame, Instruction inst, byte opcode) {
        Method method = frame.method();
        String className = method.clazz().name();
        String methodName = method.name();
        String outStr = (className + "." + methodName + "() \t") +
                "寄存器(指令):" + byteToHexString(new byte[]{opcode}) + " -> " + inst.getClass().getSimpleName() + " => 局部变量表:" + JSON.toJSONString(frame.operandStack().getSlots()) + " 操作数栈:" + JSON.toJSONString(frame.operandStack().getSlots());
        System.out.println(outStr);
    }

    private static String byteToHexString(byte[] codes) {
        StringBuilder sb = new StringBuilder();
        sb.append("0x");
        for (byte b : codes) {
            int value = b & 0xFF;
            String strHex = Integer.toHexString(value);
            if (strHex.length() < 2) {
                strHex = "0" + strHex;
            }
            sb.append(strHex);
        }
        return sb.toString();
    }

}

HelloWorld.java

public class HelloWorld {

    public static void main(String[] args) {
        long x = fibonacci(10);
        System.out.println(x);
    }

    //斐波那契数列(Fibonacci sequence)
    private static long fibonacci(long n) {
        if (n <= 1) {
            return n;
        } else {
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }

}

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。

测试结果 {jvm执行复杂计算:斐波那契数列,输出结果55}

HelloWorld.fibonacci 指令:

lload_0
lconst_1
lcmp
ifgt 7
lload_0
lreturn
lload_0
lconst_1
lsub
invokestatic org/itstack/demo/test/HelloWorld/fibonacci(J)J
lload_0
ldc2_w 2
lsub
invokestatic org/itstack/demo/test/HelloWorld/fibonacci(J)J
ladd
lreturn

执行过程:

org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0x14 -> LDC2_W => 局部变量表:[{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表:[{"num":10},{"num":0},{"num":0}] 操作数栈:[{"num":10},{"num":0},{"num":0}]
java/lang/Object.<clinit>() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表null 操作数栈null
java/lang/Object.<clinit>() 	寄存器(指令):0xb1 -> RETURN => 局部变量表null 操作数栈null
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表:[{"num":10},{"num":0},{"num":0}] 操作数栈:[{"num":10},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x0a -> LCONST_1 => 局部变量表:[{"num":10},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":10},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x94 -> LCMP => 局部变量表:[{"num":10},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":10},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x9d -> IFGT => 局部变量表:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x0a -> LCONST_1 => 局部变量表:[{"num":10},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":10},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x65 -> LSUB => 局部变量表:[{"num":10},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":10},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x0a -> LCONST_1 => 局部变量表:[{"num":9},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":9},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x94 -> LCMP => 局部变量表:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x9d -> IFGT => 局部变量表:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x0a -> LCONST_1 => 局部变量表:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x65 -> LSUB => 局部变量表:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":9},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x0a -> LCONST_1 => 局部变量表:[{"num":8},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":8},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x94 -> LCMP => 局部变量表:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x9d -> IFGT => 局部变量表:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x0a -> LCONST_1 => 局部变量表:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x65 -> LSUB => 局部变量表:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":8},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表:[{"num":7},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":7},{"num":0},{"num":1},{"num":0},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x1e -> LLOAD_0 => 局部变量表:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0},{"num":0},{"num":0},{"num":0},{"num":0}]

... ...

org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xad -> LRETURN => 局部变量表:[{"num":1},{"num":0},{"num":0},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":1},{"num":0},{"num":0},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x61 -> LADD => 局部变量表:[{"num":2},{"num":0},{"num":1},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":2},{"num":0},{"num":1},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xad -> LRETURN => 局部变量表:[{"num":3},{"num":0},{"num":1},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":3},{"num":0},{"num":1},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x61 -> LADD => 局部变量表:[{"num":5},{"num":0},{"num":3},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":5},{"num":0},{"num":3},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xad -> LRETURN => 局部变量表:[{"num":8},{"num":0},{"num":3},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":8},{"num":0},{"num":3},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x61 -> LADD => 局部变量表:[{"num":13},{"num":0},{"num":8},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":13},{"num":0},{"num":8},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xad -> LRETURN => 局部变量表:[{"num":21},{"num":0},{"num":8},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":21},{"num":0},{"num":8},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0x61 -> LADD => 局部变量表:[{"num":34},{"num":0},{"num":21},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":34},{"num":0},{"num":21},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.fibonacci() 	寄存器(指令):0xad -> LRETURN => 局部变量表:[{"num":55},{"num":0},{"num":21},{"num":0},{"num":2},{"num":0}] 操作数栈:[{"num":55},{"num":0},{"num":21},{"num":0},{"num":2},{"num":0}]
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0x40 -> LSTORE_1 => 局部变量表:[{"num":55},{"num":0},{"num":0}] 操作数栈:[{"num":55},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0xb2 -> GET_STATIC => 局部变量表:[{"num":55},{"num":0},{"num":0}] 操作数栈:[{"num":55},{"num":0},{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb8 -> INVOKE_STATIC => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0x01 -> ACONST_NULL => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb3 -> PUT_STATIC => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0x01 -> ACONST_NULL => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb3 -> PUT_STATIC => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0x01 -> ACONST_NULL => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb3 -> PUT_STATIC => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0x01 -> ACONST_NULL => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb3 -> PUT_STATIC => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0x01 -> ACONST_NULL => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb3 -> PUT_STATIC => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
java/lang/System.<clinit>() 	寄存器(指令):0xb1 -> RETURN => 局部变量表:[{"num":0}] 操作数栈:[{"num":0}]
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0xb2 -> GET_STATIC => 局部变量表:[{"num":55},{"num":0},{"num":0}] 操作数栈:[{"num":55},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0x1f -> LLOAD_1 => 局部变量表:[{"num":55},{"num":0},{"num":0}] 操作数栈:[{"num":55},{"num":0},{"num":0}]
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0xb6 -> INVOKE_VIRTUAL => 局部变量表:[{"num":55},{"num":55},{"num":0}] 操作数栈:[{"num":55},{"num":55},{"num":0}]
55
org/itstack/demo/test/HelloWorld.main() 	寄存器(指令):0xb1 -> RETURN => 局部变量表:[{"num":55},{"num":55},{"num":0}] 操作数栈:[{"num":55},{"num":55},{"num":0}]

上一篇:用Java实现JVM第六章《类和对象》

下一篇:用Java实现JVM第八章《数组和字符串》

微信搜索「bugstack虫洞栈」公众号,关注后回复「用Java实现jvm源码」获取本文源码&更多原创专题案例!

📝 首页

🌏 知识星球码农会锁

实战项目:「DDD+RPC分布式抽奖系统」、专属小册、问题解答、简历指导、架构图稿、视频课程

🐲 头条

⛳ 目录

  1. 源码 - :octocat: 公众号:bugstack虫洞栈 文章所涉及到的全部开源代码
  2. Java
  3. Spring
  4. 面向对象
  5. 中间件
  6. Netty 4.x
  7. 字节码编程
  8. 💯实战项目
  9. 部署 Dev-Ops
  10. 📚PDF 下载
  11. 关于

💋 精选

🐾 友链

建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。

作者小傅哥
链接https://bugstack.cn
来源bugstack虫洞栈

2021年10月24日,小傅哥 的文章全部开源到代码库 CodeGuide 中,与同好同行,一起进步,共同维护。

这里我提供 3 种方式:

  1. 提出 Issue :在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交 PR )。
  2. 处理 Issue : 帮忙处理一些待处理的 Issue
  3. 提交 PR: 对于错别字/笔误这类问题可以直接提交PR,无需提交Issue 确认。

详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹

  • 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
微信:fustack

  • 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
公众号:bugstack虫洞栈

感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。

Clone this wiki locally