jvm造轮子(jvm zgc)

  本篇文章为你整理了jvm造轮子(jvm zgc)的详细内容,包含有java轮子是什么意思 jvm zgc jvm juc jvm工具 jvm造轮子,希望能帮助你了解 jvm造轮子。

  博客内容来源于 刘欣老师的课程,刘欣老师的公众号 码农翻身

  博客内容来源于 Java虚拟机规范(JavaSE7)

  博客内容的源码https://gitee.com/zumengjie/litejvm

  阅读此博客请配合源码食用。

  ClassLoader

  Class

  MethodArea
ExecutorEngine
StackFrame
ExecutionResult
Heap

  ClassLoader

  ClassLoader就是根据类名去classpath路径上找到Class文件然后解析Class文件形成Class类对象。

  

package com.datang.litejvm.loader;

 

  import com.datang.litejvm.clz.Class;

  import org.apache.commons.io.IOUtils;

  import org.apache.commons.lang3.StringUtils;

  import java.io.*;

  import java.util.ArrayList;

  import java.util.List;

  
public byte[] readBinaryCode(String className) {

   className = className.replace(., File.separatorChar) + ".class";

   for (String path : this.clzPaths) {

   String clzFileName = path + File.separatorChar + className;

   byte[] codes = loadClassFile(clzFileName);

   if (codes != null) {

   return codes;

   return null;

   * @author: 顶风少年

   * @Description: 根据类路径读取class文件

   * @date: 21:54 2022/6/8

   private byte[] loadClassFile(String clzFileName) {

   File f = new File(clzFileName);

   try {

   return IOUtils.toByteArray(new FileInputStream(f));

   } catch (IOException e) {

   e.printStackTrace();

   return null;

   * @author: 顶风少年

   * @Description: 解析class文件形成Class对象,在这里是形成ClassFile对象

   * @date: 13:55 2022/6/10

   public Class loadClass(String className) {

   byte[] codes = this.readBinaryCode(className);

   ClassParser parser = new ClassParser();

   return parser.parse(codes);

  }

 

  View Code

  Class

  首先我们对一个已经编译完成的class文件进行解析,解析的过程就是读取每一个字节,然后了解其结构以及含义,将其解析为一个Class类。使用javap -v xxx.class命令可以查看该class的结构。

  Class字节码文件的内容十分紧凑,首先是魔数,小版本号,大版本号,常量池,类访问权限,类,父类,父接口,字段列表,方法列表。常量池占了大部分的字节码文件,其余的部分很多都会引用常量池项。

  解析Class文件就是读取固定长度的字节,魔数为4个字节,小版本号2个字节,大版本号两个字节。接下来的常量池稍微复杂,两个字节标记了常量池项的总个数,其中每一个常量池项都有对应的数据结构。需要先读取1个字节判断它的结构,例如tag是1则它是一个CONSTANT_Utf8_info,这是一个最简单的结构,2个字节的长度表示后续字符串的长度,然后再读取对应长度的字节数。常量池项是可以引用常量池项的,例如第7项是一个CONSTANT_Class_info这个结构包含了2个字节的index它指向常量池的第40项目,是Class对应的类名。解析常量池时需要根据tag判断常量池项是什么结构,然后再根据具体的结构读取字节。

  接下来是读取2个字节类的访问权限,无论是类,字段,方法都有访问权限,注意访问权限可能有多个,例如类可以是 public final

  接下来是2个字节的类名,2个字节的父类名,2个字节的接口个数,每个接口名也是2个字节。

  之后是类成员变量也就是字段,2个字节的成员个数,每个字段里包含2个字节的访问权限,2个字节的字段名,2个字节的字段类型,两个字节的的属性个数,这个属性也是有多种结构的,方法里也有,放到方法里说。

  字节码文件的最后一部分是方法,2个字节的方法个数,每个方法包含2个字节的访问权限,2个字节的方法名,2个字节的方法签名(入参和返回值)。2个字节的属性个数,每个属性中包含,2个字节的属性名称。在方法中只有一个属性就是Code。

  jvm定义的属性有以下结构,字段的属性也在其中。

  

  首先是2个字节的属性名称,根据属性名判断属性。每个属性都有4个字节的属性长度,它标记了接下来的属性内容的字节数。Code属性中包含2个字节的栈深度,2个字节的局部变量表,4个字节的字节码长度,字节码长度为N则表示有N个字节的字节码指令,每个指令1个字节。对于字节码指令需要展开说,每个字节码指令根据其含义的不同可能带有不同的参数,例如bb含义为new对象,bb的后边有2个字节是它的参数,这两个参数指向了常量池中的常量项是需要new的对象类名。

  在字节码指令后是2个字节的异常长度,异常的相关的内容,我本次没有解析,只是手动跳过了异常。

  Code属性中还包含了2个其他属性。LineNumberTable和LocalVariableTable其一是行号相关,其二是参数列表。

  2个字节区分属性的名称,4个字节表示属性内容的长度。LineNumberTable有2个字节的行数,每一行中包含2个字节的起始位,2个字节的行号。

  LocalVariableTable有2个字节的参数个数,每个参数有2个字节的起始位,2个字节的长度,2个字节的属性名,2个字节的下标。

  以上是Class文件中可以解析出来的内容,当然根据Class的复杂程度,解析出来的内容不同,我这里是最基本的Class属性,将其解析完毕后,形成一个Class类。

  

package com.datang.litejvm.clz;

 

  
import com.datang.litejvm.constant.ConstantClassInfo;

  import com.datang.litejvm.constant.ConstantPool;

  import com.datang.litejvm.constant.ConstantUTF8Info;

  import java.util.Iterator;

  import java.util.List;

   * @author: 顶风少年

   * @Description: Class类

   * @date: 13:55 2022/6/10

  public class Class {

  
public void setAccessFlag(ClassAccessFlag classAccessFlag) {

   this.classAccessFlag = classAccessFlag;

   * @author: 顶风少年

   * @Description: 类

   * @date: 17:18 2022/6/10

   public int getThisClassIndex() {

   return thisClassIndex;

   public void setThisClassIndex(int thisClassIndex) {

   this.thisClassIndex = thisClassIndex;

   * @author: 顶风少年

   * @Description: 父类

   * @date: 17:18 2022/6/10

   public int getSuperClassIndex() {

   return superClassIndex;

   public void setSuperClassIndex(int superClassIndex) {

   this.superClassIndex = superClassIndex;

   ConstantClassInfo constantClassInfo = (ConstantClassInfo) pool.getConstantInfo(superClassIndex);

   this.superClassName = constantClassInfo.getClassName();

   public String getSuperClassName() {

   return superClassName;

   * @author: 顶风少年

   * @Description: 接口

   * @date: 17:18 2022/6/10

   public List Integer getInterfaceIndexList() {

   return interfaceIndexList;

   public void setInterfaceIndexList(List Integer interfaceIndexList) {

   this.interfaceIndexList = interfaceIndexList;

   * @author: 顶风少年

   * @Description: 属性列表

   * @date: 11:22 2022/6/12

   public List Field getFieldList() {

   return fieldList;

   * @author: 顶风少年

   * @Description: 属性列表

   * @date: 11:22 2022/6/12

   public void setFieldList(List Field fieldList) {

   this.fieldList = fieldList;

   * @author: 顶风少年

   * @Description: 方法

   * @date: 18:31 2022/6/12

   public List Method getMethodList() {

   return methodList;

   * @author: 顶风少年

   * @Description: 方法

   * @date: 18:31 2022/6/12

   public void setMethodList(List Method methodList) {

   this.methodList = methodList;

  
public Method getMethod(String methodName, String paramAndResultType) {

   Method rMethod = null;

   Iterator Method iter = methodList.iterator();

   while (iter.hasNext()) {

   Method method = iter.next();

   int nameIndex = method.getNameIndex();

   int descriptorIndex = method.getDescriptorIndex();

   ConstantUTF8Info nameInfo = (ConstantUTF8Info) pool.getConstantInfo(nameIndex);

   ConstantUTF8Info descriptorInfo = (ConstantUTF8Info) pool.getConstantInfo(descriptorIndex);

   if (nameInfo.getBytes().equals(methodName) descriptorInfo.getBytes().equals(paramAndResultType)) {

   rMethod = method;

   return rMethod;

   * @author: 顶风少年

   * @Description: 查询main方法

   * @date: 10:36 2022/6/16

   public Method getMainMethod() {

   return getMethod("main", "([Ljava/lang/String;)V");

  }

 

  View Code

  MethodArea

  方法区中有个Map它的key是class类名,value是Class对象。使用ClassLoader解析后的Class对象都存在这里。

  

package com.datang.litejvm.engin;

 

  
import com.datang.litejvm.clz.Method;

  import com.datang.litejvm.constant.ConstantFieldRefInfo;

  import com.datang.litejvm.constant.ConstantMethodRefInfo;

  import com.datang.litejvm.loader.ClassLoader;

  import com.datang.litejvm.clz.Class;

  import java.util.HashMap;

  import java.util.Map;

   * @author: 顶风少年

   * @Description: 方法区

   * @date: 10:49 2022/6/16

  public class MethodArea {

   public static final MethodArea instance = new MethodArea();

   * 注意:我们做了极大的简化, ClassLoader 只有一个, 实际JVM中的ClassLoader,是一个双亲委托的模型

   private ClassLoader classLoader = null;

   * @author: 顶风少年

   * @Description: 存放所有的Class

   * @date: 10:39 2022/6/16

   Map String, Class map = new HashMap String, Class ();

   private MethodArea(){

   * @author: 顶风少年

   * @Description: 单例,获取常量池

   * @date: 10:39 2022/6/16

   public static MethodArea getInstance(){

   return instance;

   public void setClassFileLoader(ClassLoader clzLoader){

   this.classLoader = clzLoader;

   * @author: 顶风少年

   * @Description: 获取main方法

   * @date: 10:39 2022/6/16

   public Method getMainMethod(String className){

   Class clz = this.findClass(className);

   return clz.getMainMethod();

   * @author: 顶风少年

   * @Description: 从指定class中 根据名称获取方法

   * @date: 22:23 2022/6/16

   public Method getMethod(ConstantMethodRefInfo constantMethodRefInfo){

   Class aClass = findClass(constantMethodRefInfo.getClassName());

   Method method = aClass.getMethod(constantMethodRefInfo.getMethodName(), constantMethodRefInfo.getParamAndResult());

   return method;

   * @author: 顶风少年

   * @Description: 创建Class

   * @date: 10:38 2022/6/16

   public Class findClass(String className){

   if(map.get(className) != null){

   return map.get(className);

   // 看来该class 文件还没有load过

   Class aClass = this.classLoader.loadClass(className);

   map.put(className, aClass);

   return aClass;

  }

 

  View Code

  ExecutorEngine

  执行引擎中主要有一个栈结构,栈中的每个元素是StackFrame栈帧。执行引擎执行第一步将main方法压入栈中,然后就是执行栈帧。每个栈帧其实就是一个method当方法执行结束后返回ExecutionResult里边封装了该方法是暂存运行其他method还是方法运行结束出栈。如果是运行其他的method则将跳转方法压入栈中,并且传递参数,如果是方法执行结束则将当前方法出栈。执行引擎就是在不断的判断栈内是否还有元素,如果栈为空则表示当前程序执行结束。

  

package com.datang.litejvm.engin;

 

  import com.datang.litejvm.clz.Method;

  import java.util.ArrayList;

  import java.util.List;

  import java.util.Stack;

   * @author: 顶风少年

   * @Description: 执行引擎

   * @date: 15:19 2022/6/16

  public class ExecutorEngine {

   * @author: 顶风少年

   * @Description: 栈

   * @date: 15:41 2022/6/16

   private Stack StackFrame stack = new Stack StackFrame ();

  
StackFrame nextFrame = StackFrame.create(nextMethod);

   nextFrame.setCallerFrame(frame);

   setupFunctionCallParams(frame, nextFrame);

   //将新的栈帧也入栈

   stack.push(nextFrame);

   } else {

   //出栈

   stack.pop();

   * @author: 顶风少年

   * @Description: 给下个调用方法设置参数

   * @date: 16:07 2022/6/16

   private void setupFunctionCallParams(StackFrame currentFrame, StackFrame nextFrame) {

   Method nextMethod = nextFrame.getMethod();

   //获取参数列表

   List String parameterList = nextMethod.getParameterList();

   List JavaObject values = new ArrayList ();

   //要添加 this

   int paramNum = parameterList.size() + 1;

   while (paramNum 0) {

   values.add(currentFrame.getOperandStack().pop());

   paramNum--;

   List JavaObject params = new ArrayList ();

   for (int i = values.size() - 1; i i--) {

   params.add(values.get(i));

   //设置局部变量表

   nextFrame.setLocalVariableTable(params);

  }

 

  View Code

  StackFrame

  每个方法入栈都会形成一个StackFrame,栈帧中包含两个数据结构。局部变量表和操作数栈,局部变量表是用来存放方法的入参,方法内的计算结果,操作数栈则是真正运算的地方,我们经常说的 1 + 2 = 3 的操作其实是在操作数栈进行的,先将 1 和 2 压入操作数栈,将 1 和 2 出栈进行运算最后得出的 3 将其再次压入操作数栈。StackFrame执行时就是从method总取出Code属性中的操作指令,一条一条的执行。每条指令执行结束后会对ExecutionResult进行设置。

  

package com.datang.litejvm.engin;

 

  import com.datang.litejvm.clz.Method;

  import com.datang.litejvm.cmd.ByteCodeCommand;

  import java.util.ArrayList;

  import java.util.List;

  import java.util.Stack;

   * @author: 顶风少年

   * @Description: 函数栈帧

   * @date: 15:41 2022/6/16

  public class StackFrame {

   //局部变量表

   private List JavaObject localVariableTable = new ArrayList JavaObject ();

   //操作数栈

   private Stack JavaObject operandStack = new Stack JavaObject ();

   //字节码指令偏移量,指向下一个操作指令

   int index = 0;

   //当前方法

   private Method m = null;

   //上一个函数栈帧

   private StackFrame callerFrame = null;

   private StackFrame(Method m) {

   this.m = m;

   //创建函数栈帧

   public static StackFrame create(Method m) {

   StackFrame frame = new StackFrame(m);

   return frame;

   * @author: 顶风少年

   * @Description: 上一个函数栈帧

   * @date: 16:44 2022/6/16

   public StackFrame getCallerFrame() {

   return callerFrame;

   public void setCallerFrame(StackFrame callerFrame) {

   this.callerFrame = callerFrame;

   * @author: 顶风少年

   * @Description: 栈帧所属方法

   * @date: 16:45 2022/6/16

   public Method getMethod() {

   return m;

   * @author: 顶风少年

   * @Description: 设置局部变量表

   * @date: 16:40 2022/6/16

   public void setLocalVariableTable(List JavaObject values) {

   this.localVariableTable = values;

   * @author: 顶风少年

   * @Description: 获取局部变量表中的某个变量

   * @date: 10:22 2022/6/17

   public JavaObject getLocalVariableValue(int index) {

   return this.localVariableTable.get(index);

   * @author: 顶风少年

   * @Description: 向局部变量表设置值

   * @date: 10:38 2022/6/17

   public void setLocalVariableValue(int index, JavaObject jo) {

   //问题: 为什么要这么做??

   if (this.localVariableTable.size() - 1 index) {

   for (int i = this.localVariableTable.size(); i = index; i++) {

   this.localVariableTable.add(null);

   this.localVariableTable.set(index, jo);

  
public ExecutionResult execute() {

   List ByteCodeCommand cmds = m.getCodeAttr().getCmds();

   while (index cmds.size()) {

   //执行结果

   ExecutionResult result = new ExecutionResult();

   //下一条字节码指令

   ByteCodeCommand cmd = cmds.get(index);

   System.out.println(cmd.toString());

   //执行

   cmd.execute(this, result);

   //运行下一条

   if (result.isRunNextCmd()) {

   index++;

   } else if (result.isExitCurrentFrame()) {

   //退出当前栈帧,return 剩余的栈帧不执行了

   return result;

   } else if (result.isPauseAndRunNewFrame()) {

   //暂停当前栈帧,执行新的函数栈帧

   index++;

   return result;

   } else if (result.isJump()) {

   //跳转指令,跳转到下一个字节码指令

   int offset = result.getNextCmdOffset();

   //设置下一个指令的偏移量

   this.index = getNextCommandIndex(offset);

   } else {

   index++;

   //如果循环走完了,说明没有任何的跳转,停止,表示当前StackFrame的指令全部执行完毕,可以退出了

   ExecutionResult result = new ExecutionResult();

   result.setNextAction(ExecutionResult.EXIT_CURRENT_FRAME);

   return result;

   * @author: 顶风少年

   * @Description: 根据偏移量查询字节码指令

   * @date: 17:48 2022/6/16

   public int getNextCommandIndex(int offset) {

   List ByteCodeCommand cmds = m.getCodeAttr().getCmds();

   for (int i = 0; i cmds.size(); i++) {

   if (cmds.get(i).getOffset() == offset) {

   return i;

   throw new RuntimeException("Cant find next command");

   * @author: 顶风少年

   * @Description: 获取操作数栈

   * @date: 19:04 2022/6/16

   public Stack JavaObject getOperandStack() {

   return this.operandStack;

  
ExecutionResult

  操作指令执行结束后会设置ExecutionResult,这个类标记了当前字节码执行后的行为,默认的是RUN_NEXT_CMD继续执行下一条字节码指令,也可能是JUMP跳转到指定的指令号,EXIT_CURRENT_FRAME停止执行,PAUSE_AND_RUN_NEW_FRAME暂存当前栈帧,执行新的栈帧。如果是JUMP或RUN_NEXT_CMD则当前栈帧继续执行。如果是EXIT_CURRENT_FRAME和PAUSE_AND_RUN_NEW_FRAME就要返回到执行引擎层。

  

package com.datang.litejvm.engin;

 

  import com.datang.litejvm.clz.Method;

   * @author: 顶风少年

   * @Description: 执行结果

   * @date: 17:29 2022/6/16

  public class ExecutionResult {

   //默认值 执行下一条指令

   public static final int RUN_NEXT_CMD = 1;

   //跳转指令

   public static final int JUMP = 2;

   //退出当前栈帧,剩余指令不执行了

   public static final int EXIT_CURRENT_FRAME = 3;

   //暂停当前栈帧,执行新的函数栈帧

   public static final int PAUSE_AND_RUN_NEW_FRAME = 4;

   //下一次的行为

   private int nextAction = RUN_NEXT_CMD;

   //如果是跳转指令 JUMP ,则会记录下一个指令偏移量

   private int nextCmdOffset = 0;

   //如果是执行新的栈帧 EXIT_CURRENT_FRAME 则需要设置栈帧对应的方法

   private Method nextMethod;

   * @author: 顶风少年

   * @Description: 获取下一个执行的方法

   * @date: 17:46 2022/6/16

   public Method getNextMethod() {

   return nextMethod;

   public void setNextMethod(Method nextMethod) {

   this.nextMethod = nextMethod;

   * @author: 顶风少年

   * @Description: 设置字节码执行后的行为, 默认为 RUN_NEXT_CMD 执行下一条指令

   * @date: 17:31 2022/6/16

   public void setNextAction(int action) {

   this.nextAction = action;

   * @author: 顶风少年

   * @Description: 下一个字节码指令偏移量

   * @date: 17:43 2022/6/16

   public int getNextCmdOffset() {

   return nextCmdOffset;

   public void setNextCmdOffset(int nextCmdOffset) {

   this.nextCmdOffset = nextCmdOffset;

  
public boolean isPauseAndRunNewFrame() {

   return this.nextAction == PAUSE_AND_RUN_NEW_FRAME;

   public boolean isExitCurrentFrame() {

   return this.nextAction == EXIT_CURRENT_FRAME;

   public boolean isRunNextCmd() {

   return this.nextAction == RUN_NEXT_CMD;

   public boolean isJump() {

   return this.nextAction == JUMP;

  }

 

  View Code

  Heap

  堆这个对象用来模拟创建不同类型的对象,当前可以创建String,int,float,Object。如果创建的Object则还可以给Object设置属性。

  

package com.datang.litejvm.engin;

 

   * @author: 顶风少年

   * @Description: 堆,可以有多种类型,对象,字符串,int float

   * @date: 21:02 2022/6/16

  public class Heap {

   * 没有实现垃圾回收, 所以对于下面新创建的对象, 并没有记录到一个数据结构当中

   private static Heap instance = new Heap();

   private Heap() {

   public static Heap getInstance(){

   return instance;

   public JavaObject newObject(String clzName){

   JavaObject jo = new JavaObject(JavaObject.OBJECT);

   jo.setClassName(clzName);

   return jo;

   public JavaObject newString(String value){

   JavaObject jo = new JavaObject(JavaObject.STRING);

   jo.setStringValue(value);

   return jo;

   public JavaObject newFloat(float value){

   JavaObject jo = new JavaObject(JavaObject.FLOAT);

   jo.setFloatValue(value);

   return jo;

   public JavaObject newInt(int value){

   JavaObject jo = new JavaObject(JavaObject.INT);

   jo.setIntValue(value);

   return jo;

  }

 

  View Code

  

  以上就是jvm造轮子(jvm zgc)的详细内容,想要了解更多 jvm造轮子的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: