项目地址:

https://github.com/aprz512/write-your-own-jvm

数组介绍

数组在Java虚拟机中是个比较特殊的概念。为什么这么说呢?有下面几个原因:

首先,数组类和普通的类是不同的。普通的类从class文件中加载,但是数组类由Java虚拟机在运行时生成。数组的类名是左方括号([)+数组元素的类型描述符;数组的类型描述符就是类名本身。例如,int[]的类名是[Iint[][]的类名是[[IObject[]的类名是[Ljava/lang/Object;String[][]的类名是[[java/lang/String;等等。

其次,创建数组的方式和创建普通对象的方式不同。普通对象由new指令创建,然后由构造函数初始化。基本类型数组由newarray指令创建;引用类型数组由anewarray指令创建;另外还有一个专门的multianewarray指令用于创建多维数组。

最后,数组和普通对象存放的数据也是不同的。普通对象中存放的是实例变量,通过putfieldgetfield指令存取。数组对象中存放的则是数组元素,通过<t>aload<t>astore系列指令按索引存取。其中<t>可以是a、b、c、d、f、i、l或者s,分别用于存取引用、byte、char、double、float、int、long或short类型的数组。另外,还有一个arraylength指令,用于获取数组长度。

创建数组的Class

一般来说,一个 MyClass 实例对应一个 class 文件,但是数组的 Class 是没有对应的 class 文件的。所以我们直接创建一个 MyClass 对象,给它设置一个 className 就好了。

我们实现一下 ANewArray 指令,先看例子:

String[] argss = new String[4];

看看编译后的字节码:

Untitled

anewarray 后面跟了一个类名 String。表示需要创建String类型的数组,数组的大小由前一条指令给出,也就是4。

看看代码实现:

		@Override
    public void execute(StackFrame frame) {
        int count = frame.getOperandStack().popInt();
        if (count < 0) {
            throw new MyJvmException("java.lang.NegativeArraySizeException");
        }
        ConstantPool constantPool = frame.getMyMethod().getMyClass().getConstantPool();
        // must be a symbolic reference to a class, array, or interface type.
        ConstantPool.Constant constant = constantPool.getConstant(operand);
        ClassRef classRef = (ClassRef) constant.value;
        MyClass resolvedClass = classRef.getResolvedClass();
        MyClass arrayClass = resolvedClass.toArrayClass();
        ArrayObject arrayObject = arrayClass.newArrayObject(count);
        frame.getOperandStack().pushRef(arrayObject);
    }

逻辑很简单,resolvedClass 就是上面例子中的 String 类。我们需要根据这个类名创建出数组的类名,具体的规则就是:

// [XXX -> [[XXX
// int -> [I
// XXX -> [LXXX;

有了类名后,使用 ClassLoader 加载一下,当然 ClassLoader 里面的逻辑也需要特殊处理:

		private MyClass loadArrayClass(String name) {
        MyClass myClass = MyClass.createArrayClass(name, this);
        loadedClasses.put(name, myClass);
        return myClass;
    }