这是我对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执行流程)