JVM 指令集里并没有一个官方叫“asstore”的指令,你真正要找的是 aastore(把对象引用写入数组)或者 astore(把引用写入局部变量表);日常交流中 many 人会简写、拼错成 asstore,所以我这次把两条指令都讲清楚。
JVM asstore指令是什么意思?先分清astore和aastore
JVM 字节码指令的命名有一套自己的逻辑,带 a 前缀的指令和对象引用(reference)相关,后面跟着 store 就表示“存”,但“存到哪儿”不同,指令也不同。
astore:把一个对象引用从操作数栈顶弹出,存进当前栈帧的局部变量表。aastore:把操作数栈里的对象引用,按索引写入一个对象数组。
那“asstore”是怎么回事?多数情况下是在讨论 aastore 时,少写了一个 a,aastore 的完整结构是“a (reference) + a (array) + store”,理解成“把引用存进数组”就顺了,如果你在某篇博客或反编译工具里看到 asstore,先默认为 aastore,再去对照一下上下文。
astore:引用类型的局部变量保存指令
局部变量表是 JVM 栈帧里的私有区域,用来存放方法参数和局部变量。astore 服务的对象是 String、Object、数组引用等所有引用类型。
举个典型场景:
public void demo() {
String s = "hello";
}
对应字节码里能看到:
ldc "hello" astore_1
先把字符串常量压入操作数栈,astore_1 把这个引用弹出来,放入索引为 1 的局部变量槽,类似指令还有 istore(存整数)、lstore(存 long)、fstore(存 float)、dstore(存 double),而 astore 是引用类型的专用版本。
aastore:对象数组的引用写入指令
aastore 和 astore 的操作对象不同,astore 操作的是局部变量表,aastore 操作的是数组,它把栈上的三个数据按顺序弹出来:数组引用、索引、要存的值。
用一段真实代码演示:
Object[] objs = new Object[2]; objs[0] = "Java虚拟机";
字节码中会出现:
new java/lang/Object[2] dup iconst_0 ldc "Java虚拟机" aastore
new 创建数组后,dup 复制数组引用,接着压入索引 0,再压入字符串常量,aastore 完成写入,aastore 执行完,数组引用、索引和值都会从操作数栈消失。
asstore和astore的核心区别是什么?一张表看懂

很多初学字节码的人会把这两条指令混在一起,实际上从操作数栈的消耗、写入目标到异常处理都不一样。
对比:astore vs aastore
| 对比项 | astore | aastore |
|---|---|---|
| 官方字节码 | astore(含 astore_0 到 astore_3 简化版) | aastore |
| 写入目标 | 局部变量表 | 对象数组的指定索引 |
| 操作数栈输入 | 弹出 1 个引用 | 弹出 3 个值:数组引用、索引、引用值 |
| 执行后栈变化 | 栈顶引用消失 | 栈顶三个操作数全部消失 |
| 常见异常 | 无运行时异常(但可能出现局部变量未初始化) | NullPointerException、ArrayIndexOutOfBoundsException、ArrayStoreException |
| 使用场景 | 方法内变量赋值、函数传参后的存储 | 把对象放入对象数组,Object[]、String[]、自定义类型数组 |
从操作数栈的消费方式看执行逻辑
JVM 是典型的基于操作数栈的虚拟机,每条指令的输入输出都有严格定义。astore 只吃一个操作数,而且这个操作数就是待保存的引用本身。aastore 吃三个操作数,顺序是:先数组引用,再索引,最后是值,执行引擎会先把数组引用和索引弹出,检查索引是否越界,再检查类型是否兼容,最后才把值写进数组。
有一个容易被忽略的细节:aastore 在写入前会做类型检查,如果栈顶值和数组元素类型不匹配,会抛出 ArrayStoreException,比如把 Integer 对象存进 String[],这就是 aastore 特有的保护机制,astore 不存在这种检查。
从 Java 代码触发条件看差异
- 给局部变量赋值
String s = "abc",编译后出现 astore。 - 给对象数组元素赋值
objects[0] = "abc",编译后出现 aastore。
这两个场景的源码复杂度不同,但指令差异非常清晰,你可以用 javap -c 验证,这也是我下面要说的实操方法。
Java虚拟机asstore(aastore)指令怎么用?手把手看字节码
aastore 的使用不直接写在 Java 源码里,源码里只写数组赋值,字节码里才会出现,但如果你要手动拼字节码,或者用 ASM 框架生成类,就必须理解 aastore 的操作数顺序。
用 javap 查看实际字节码
我举一个具体的操作路径,你可以在自己电脑上复现。
第一步,新建一个 ArrayDemo.java:
public class ArrayDemo {
public void setValue(Object[] arr, int index, Object value) {
arr[index] = value;
}
}

第二步,编译:
javac ArrayDemo.java
第三步,反编译:
javap -c ArrayDemo
你会看到 setValue 方法里有这一行:
aastore
这就是 asstore 的真实形态,在这个方法里,arr[index] = value 被编译成先按加载指令把 arr、index、value 依次压入操作数栈,再调用 aastore。
手写字节码时如何保证正确
如果你使用 ASM 的 MethodVisitor 来生成字节码,写入对象数组的代码会像下面这样:
methodVisitor.visitVarInsn(ALOAD, 1); // 加载数组引用 methodVisitor.visitVarInsn(ILOAD, 2); // 加载索引 methodVisitor.visitVarInsn(ALOAD, 3); // 加载要存储的值 methodVisitor.visitInsn(AASTORE); // 执行数组存储
注意顺序:先数组,再索引,最后值,顺序一旦错了,要么抛异常,要么把数据写到错误的索引位置,行业共识认为,aastore 的顺序是 JVM 字节码新手最容易踩坑的地方,AST 转换和插桩工具里尤其常见。
用 ASM 库生成包含 aastore 的完整方法
下面是一个更完整的 ASM 片段,用来生成一个方法,它创建长度为 1 的 Object[],并写入一个字符串:
mv.visitTypeInsn(NEW, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_1);
mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
mv.visitInsn(DUP);
mv.visitInsn(ICONST_0);
mv.visitLdcInsn("hello");
mv.visitInsn(AASTORE);
mv.visitInsn(ARETURN);
这里 NEW 创建数组对象,DUP 复制引用,ICONST_1 指定数组长度,ANEWARRAY 把数组引用压回栈,随后的 DUP、ICONST_0 和 LDC 分别准备数组引用、索引和值,AASTORE 一把梭,整个序列没有局部变量表参与,完全依赖操作数栈完成写入。
aastore 在 JVM 执行引擎里的处理流程
看清楚 aastore 怎么用之后,再深入一步:JVM 执行这条指令时到底做了什么?
第一,弹出数组引用,如果是 null,立即抛出 NullPointerException,不会进入下一步。
第二,弹出索引,JVM 会比较索引和数组长度,如果小于 0 或者大于等于数组长度,抛出 ArrayIndexOutOfBoundsException。
第三,弹出要存储的引用值,并且检查它的类型是否和数组元素类型兼容,这一步有两条检查路径:
- 如果数组引用是引用类型数组,
,检查要存的值能否转换成
String[]
String。 - 如果数组本身是接口数组或抽象类型数组,则检查运行时类型是否满足接口或父类约束。
第四,类型检查通过后,把值写入数组元素,整个过程发生在 JVM 的堆上,因为所有数组对象都在堆中分配。
这个流程不是只服务于 aastore,bastore、iastore、sastore 等都遵循同样的“数组引用、索引、值”三操作数模型,只是数据类型不同。
aastore 和 iastore 的异常处理差异
aastore 是唯一的引用数组存储指令,iastore 是 int 数组存储指令,它们都会做 null 检查和索引检查,但只有 aastore 会做类型兼容检查,iastore 只需要确认栈顶是 int 类型就能写入,因为 JVM 已经通过字节码验证阶段保证类型安全,aastore 之所以多一步,是因为引用类型存在继承和接口关系,编译期类型和运行时类型可能不一致。
Q&A:关于Java虚拟机asstore的常见疑问
asstore和aastore到底是什么关系?
asstore 不是官方指令名,官方指令是 aastore,aastore 的全称是“reference array store”,表示把引用存进对象数组。“asstore”只是少写了一个 a 的非官方写法,也可能是把 astore 和 aastore 混淆后的缩写,看到 asstore,就按 aastore 理解即可。
aastore 在什么情况下会抛ArrayStoreException?
当你要存储的对象的运行时类型和数组元素的类型不兼容时,例如有一个 String[],却尝试写入 Integer 对象,JVM 在执行 aastore 时,会检查要存的值能否转换成数组元素的类型,转换失败就抛 ArrayStoreException,这个异常和索引无关,索引越界是另一个异常。
怎么快速确认一段字节码里有没有asstore(aastore)指令?
直接用 javap -c 或者 javap -v 查看 .class 文件,搜索输出中的 aastore 关键字即可,如果是 IDE 环境,在 IntelliJ IDEA 里打开 .class 文件,也会自动展示字节码视图,按 Cmd+F 或 Ctrl+F 搜 aastore,你要是用 ASM 或者 Byte Buddy 做字节码插桩,在调试时打印 MethodVisitor 的访问事件,也能在 visitInsn 回调里拦截到 AASTORE 操作码。
回到最初的问题:asstore 不是 JVM 官方指令,它的正确打开方式是 aastore,使用它的关键是记住三个操作数的顺序:数组引用、索引、要存储的值,并且理解它和 astore 的写入目标完全不同,下次你在字节码里见到 asstore,只要想到 aastore,再把操作数栈的顺序理顺,问题就解决了。