从字节码层面去看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这样一个方法并进行执行。

为什么newinvokespecial之间还有一个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的引用,而且甚至局部变量表中也还保存了引用的名称。

上一篇下一篇

猜你喜欢

热点阅读