這是我對jvm的理解,可能會有問題,望大佬發現後及時指正,誤人子弟就不好了
jvm對數據的存放進行了劃分,方法區用來存放類的信息,堆區存放初始化後的類
然後每個線程在執行方法時,會創建java棧,程序計數器,本地方法棧
java棧裏面又包含了局部變量表(用於對方法裏面的局部變量進行操作)/操作數棧(存放臨時計算結果)/常量池(類裏面的靜態數據)/返回地址
jvm執行流程(這是我寫的jvm的執行流程,可能和sun的有不同)
目錄
首先需要初始化運行環境
- 將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); } }
- 然後掃描傳遞過來的用戶class路徑,將裏面的所有class信息放到方法區
- 然後對要執行的類進行初始化
- 如果這個類有他爹,就先初始化他爹
- 然後遍歷每個方法,把字節碼什麼的整進去
- 放到堆裏面
- 執行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方法
- 首先創建線程私有的數據
- 然後把常量池和執行這個方法的參數傳進去
- 然後遍歷每個表示指令的枚舉,並且invoke指令就行了
- 指令會對常量池/操作數棧/局部變量表進行操作
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執行流程)