手写jvm


这是我对jvm的理解,可能会有问题,望大佬发现后及时指正,误人子弟就不好了

jvm对数据的存放进行了划分,方法区用来存放类的信息,堆区存放初始化后的类

然后每个线程在执行方法时,会创建java栈,程序计数器,本地方法栈

java栈里面又包含了局部变量表(用于对方法里面的局部变量进行操作)/操作数栈(存放临时计算结果)/常量池(类里面的静态数据)/返回地址


jvm执行流程(这是我写的jvm的执行流程,可能和sun的有不同)

目录

首先需要初始化运行环境

执行这个类的main方法


首先需要初始化运行环境

  1. 将jdk里面的class放到方法区,并且初始化后放到堆里
     //初始化运行依赖(jdk里面的class放到方法区和堆区)
        private void initRuntimeEnv(String jdkClassPath) throws Exception {
            //class信息放到方法区
            loadClass(new File(jdkClassPath));
            for(String className:shareData.getMethodArea().keySet()){
                System.out.println("把  "+className+"  放到堆区");
                initClassAndInflate(className);
            }
        }
  2. 然后扫描传递过来的用户class路径,将里面的所有class信息放到方法区
  3. 然后对要执行的类进行初始化
    1. 如果这个类有他爹,就先初始化他爹
    2. 然后遍历每个方法,把字节码什么的整进去
    3. 放到堆里面
    4. 执行static方法,执行空参构造
      /**
           * 初始化类并放到堆中
           *
           * @param className
           * @throws ConstantPoolException
           */
          private JvmInitedClass initClassAndInflate(String className) throws Exception {
              ClassFile classFile = shareData.getMethodArea().get(className).getClassFile();
      
              //先初始化他爹
              try {
                  String superclassName = classFile.getSuperclassName();
                  if (superclassName != null && superclassName != "") {
                      initClassAndInflate(superclassName);
                  }
              }catch (Exception e){
      
              }
              if (classFile == null) {
                  throw new ClassNotFoundException();
              }
      
              JvmInitedClass jvmInitedClass = new JvmInitedClass();
      
              jvmInitedClass.setClassFile(classFile);
              jvmInitedClass.setConstantPool(classFile.constant_pool);
              Map<Map.Entry<String, String>, JvmMethod> methodMap = new HashMap<>();
              //处理所有方法
              for (Method method : classFile.methods) {
      
                  Code_attribute codeAttribute = (Code_attribute) method.attributes.get("Code");
      
                  String name = method.getName(classFile.constant_pool);
                  System.out.println("方法名  " + name);
                  String value = method.descriptor.getValue(classFile.constant_pool);
                  System.out.println("返回值类型  " + value);
                  Code_attribute code = (Code_attribute) method.attributes.get("Code");
                  System.out.println("--方法里面的字节码数据");
      
      
                  JvmMethod jvmMethod = new JvmMethod();
                  List<Opcode> opcodes = new ArrayList<>();
                  for (int i = 0; i < code.code.length; i++) {
                      short c = (short) (0xff & code.code[i]);
                      Opcode opcode = Opcode.opcodeMap.get(c);
      
                      System.out.println("这是指令的编号 :  "+c);
      
                      //数据在常量池中索引的数组 0 号元素表示在常量池中的索引
                      byte[] operands = Arrays.copyOfRange(code.code, i + 1, i + 1 + Constants.NO_OF_OPERANDS[c]);
      
                      // TODO: 2018/8/12 这里还要获得每个指令的数组
                      //为每个用类表示的指令设置指令数组 比如 ldc 4 这就表示一个数组 0号元素是ldc 1号元素是4
                      if (opcode != null)
                          opcodes.add(opcode.setCurrentInstruction(operands));
      
                      if(opcode==null)
                          System.out.println("----------------->"+c+"   这个指令我还没处理");
                      System.out.println(c);
                  }
                  //为方法设置指令集合
                  jvmMethod.setOpcodes(opcodes);
                  jvmMethod.setMethod(method);
                  methodMap.put(new AbstractMap.SimpleEntry<>(name, value), jvmMethod);
              }
              jvmInitedClass.setMethodMap(methodMap);
      
              //存放到堆区
              shareData.getHeap().put(classFile.getName(), jvmInitedClass);
      
              //应该先执行空参构造
      
              JvmMethod staticMethod = shareData.getHeap().get(classFile.getName()).getMethodMap().get(new AbstractMap.SimpleEntry<>("<init>", "()V"));
      
              //内部类好像并没有init方法
              if(staticMethod!=null)
                  staticMethod.invoke(shareData, new ThreadPrivateData().setJavaStack(new JavaStack().setConstantPool(classFile.constant_pool)),new Object[]{});
      
              //执行jvmclass的静态代码块
              JvmMethod initMethod = shareData.getHeap().get(classFile.getName()).getMethodMap().get(new AbstractMap.SimpleEntry<>("<clinit>", "()V"));
              //把常量池弄到线程私有的数据区域
              if (initMethod != null)
                  initMethod.invoke(shareData, new ThreadPrivateData().setJavaStack(new JavaStack().setConstantPool(classFile.constant_pool)),new Object[]{});
              return jvmInitedClass;
          }

执行这个类的main方法

  1. 首先创建线程私有的数据
  2. 然后把常量池和执行这个方法的参数传进去
  3. 然后遍历每个表示指令的枚举,并且invoke指令就行了
  4. 指令会对常量池/操作数栈/局部变量表进行操作
 public void run(ShareData shareData) throws Exception {
        //获得要执行的类中的main方法
        JvmMethod jvmMethod = shareData.getHeap().get(getClassFile().getName()).getMethodMap().get(new AbstractMap.SimpleEntry<>("main", "([Ljava/lang/String;)V"));
        //线程每次执行的时候都会创建栈帧(这里面保存了局部变量表/操作数栈/计数器)
        jvmMethod.invoke(shareData,new ThreadPrivateData().setJavaStack(new JavaStack().setConstantPool(getClassFile().constant_pool)),new String[]{});
    }
//方法的执行
    public void invoke(ShareData shareData, ThreadPrivateData threadPrivateData,Object[] param) throws Exception {

        System.out.println("方法开始执行了  "+threadPrivateData.getJavaStack().getConstantPool()+"   这是常量池");

        Code_attribute codeAttribute = (Code_attribute)method.attributes.get("Code");

        //初始化局部变量表
        threadPrivateData.getJavaStack().setLocalVariometer(new LocalVariableTable(codeAttribute.max_locals).setParam(param));
        //初始化操作数栈
        threadPrivateData.getJavaStack().setOperandStack(new OperandStack(codeAttribute.max_stack));


        System.out.println("遍历每个指令并执行");
        //我还需要当前要执行的指令 比如 ldc 4 我需要从常量池获得索引为4的元素
        for(Opcode opcode:opcodes){
            opcode.invoke(shareData, threadPrivateData,opcode.getCurrentInstruction());
        }

    }

完整代码可以去我的Github(hello world还不能跑,有很多指令要处理,30多个吧,此项目仅用于理解jvm执行流程)
 

猜你喜欢

转载自blog.csdn.net/u011546032/article/details/81638389
JVM