手寫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執行流程)
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章