专栏原创出处:github-源笔记文件 (opens new window) ,github-源码 (opens new window),欢迎 Star,转载请附上原文出处链接和本声明。
Java JVM-虚拟机专栏系列笔记,系统性学习可访问个人复盘笔记-技术博客 Java JVM-虚拟机 (opens new window)
# 一、为什么 new 指令后执行 dup 指令?
在做字节码分析时,发现每次 new 指令后跟随着 dup 指令,这 2 个指令的说明:
new 创建一个对象, 并将其引用引用值压入栈顶
dup 复制栈顶数值并将复制值压入栈顶
1.创建对象的指令执行顺序)
一般我们创建一个对象使用 new Object();
,构造函数隐式的返回当前对象。
创建一个 Object 类型的对象
调用 Object 的构造函数 invokespecial<>
返回一个 Object 的实例引用
这三步对应字节码顺序为:
1. new // 创建对象后,保留一个引用,结果为: ref
2. dup // 赋值引用后,保留两个引用,结果为: ref,ref
3. invokespecial // 调用了构造方法用一个引用,结果为: ref
2.原因总结)
如果不使用 dup
复制,被构造函数指令使用后,最终无法返回实例引用。
一个不复制的实现方式:
如果不进行 dup 复制指令在 new 指令后直接将引用存入局部变量表中,后续使用时做推送至栈顶操作也可以。 但是这样在构造函数执行时,最少的一次推送至栈顶操作。(当前是不会这么做的)
# 二、构造函数返回值操作分析
new 指令后,会执行构造函数,使用了第一个引用。
程序做赋值操作。第二个引用会保存到局部变量表槽位中。比如:Object o = new Object()
程序不做赋值操作,直接操作对象的成员。则直接使用第二个引用。比如:new Object().toString()
程序不对这个对象操作,第二个引用被
pop
指令弹出
字节码实战验证:
声明一个 class 进行字节码分析
public class NewDupCode {
private int a;
public NewDupCode(int a) {
this.a = a;
}
}
- 创建对象并返回对象实例
public NewDupCode newDupCode() {
return new NewDupCode(1);
}
Code:
stack=4, locals=1, args_size=1
0: new #4 // class NewDupCode
3: dup
4: iconst_1
5: ldc #5 // String 2
7: invokespecial #6 // 调用构造函数使用 1 个
10: areturn // 返回值使用 1 个
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this LNewDupCode;
- 程序做赋值操作
public int voidUseNewDupCode() {
final NewDupCode o = new NewDupCode(1);
return o.a + o.hashCode();
}
Code:
stack=4, locals=2, args_size=1
0: new #4 // class NewDupCode
3: dup
4: iconst_1
5: ldc #5 // String 2
7: invokespecial #6 // 调用构造函数使用 1 个
10: astore_1 // 赋值给 o 时,将栈顶引用型数值 o 存入槽位 1(2 个用完)
11: aload_1 // 将 o 引用类型本地变量推送至栈顶,出来准备取值
12: getfield #2
15: aload_1 // 将 o 引用类型本地变量推送至栈顶,出来准备取值
16: invokevirtual #7
19: iadd
20: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 21 0 this LNewDupCode;
11 10 1 o LNewDupCode; // new 以后赋值给 o 存入槽位
- 程序不做赋值操作,直接操作对象的成员
public int voidUseNonRefNewDupCode() {
return new NewDupCode(1).a;
}
Code:
stack=4, locals=1, args_size=1
0: new #4 // class NewDupCode
3: dup
4: iconst_1
5: ldc #5 // String 2
7: invokespecial #6 // 调用构造函数使用 1 个
10: getfield #2 // 剩余 1 个,直接用于字段取值 Field a:I
13: ireturn
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 this LNewDupCode;
- 程序不对这个对象操作,只进行一个 new 操作
public void voidNewDupCode() {
new NewDupCode(1);
}
Code:
stack=4, locals=1, args_size=1
0: new #4 // class NewDupCode
3: dup
4: iconst_1
5: ldc #5 // String 2
7: invokespecial #6 // 调用构造函数使用 1 个
10: pop // 弹出 1 个
11: return
LocalVariableTable:
Start Length Slot Name Signature
0 12 0 this L //NewDupCode;