从字节码层面去看new一个对象的执行过程
2021-11-05 本文已影响0人
Wannay
我们一般使用如下这样的方式去创建一个对象:StringBuffer buffer = new StringBuffer();
它对应的字节码应该包括如下这些部分,算是对这个对象的创建和使用过程。
0: new #2 // class java/lang/StringBuffer
3: dup
4: invokespecial #3 // Method java/lang/StringBuffer."<init>":()V
7: astore_1
8: aload_1
先是使用new
字节码,并通过#2
去常量池找到class java/lang/StringBuffer
这样一个Class,从而去创建一个StringBuffer对象,但是这里仅仅是创建对象罢了,还是个空壳的对象,连构造器方法都没执行那种。
真正的构造器方法还是在invokespecial
字节码中去执行的,我们可以看到它是通过#3
去常量池里去找到Method java/lang/StringBuffer."<init>":()V
这样一个方法并进行执行。
为什么new
和invokespecial
之间还有一个dup
指令的存在呢?首先我们得知道dup
是什么意思?它其实是duplicate
的缩写,也就是重复的意思,也就是把刚刚创建出来的对象的引用,在栈里面再拷贝一份。
至于为什么要dup
一遍?我们首先要知道invokespecial
指令是会消耗掉刚刚new出来的对象的在操作数栈中的引用的。如果直接消耗掉了,那么我们后续怎么拿到这个对象的引用?很显然拿不到对吧,因此就得dup
一遍,这样就算栈中消耗了一份,也还会剩下来一份,后续才可以拿到该对象引用。
接着就是astore_1
指令,它的作用就是将栈顶的元素(就是我们刚刚dup
拷贝的对象引用),保存到局部变量表的1号槽位(slot 1
)中,保存的同时也会将栈顶的元素消耗掉,因此后续得通过aload_1
从局部变量表中拿到对象的引用。
我们可以查看局部变量表
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 args [Ljava/lang/String;
8 8 1 buffer Ljava/lang/StringBuffer;
我们可以发现在局部变量表的1号槽位确实放了一个buffer
的引用,而且甚至局部变量表中也还保存了引用的名称。