Java 方法 return 值的拆箱
Java unboxing on method return value
给定以下方法:
Long getLong() {
...
}
如果我调用它并将 return 值分配给这样的变量:
long abc = getLong();
是否会生成一个 Long
对象然后将其转换为 long 或 Java 是否足够聪明以避免生成中间 Long
对象?或者它实际上可能取决于 getLong()
?
的实施
我问这个的原因是包装器对象的大小通常比相应的原始类型大小大得多。如果我必须多次调用此方法并且每次都需要为 Long
对象分配内存,程序最终会消耗比实际需要更多的内存,从而触发更多的 GC 周期。
此外,我如何验证执行 long abc = getLong()
时发生的确切步骤(基本上也在寻找有关如何自己获得上述问题答案的指导)?
您问:“会生成一个 Long 对象然后将其转换为 long 还是 Java 足够聪明以避免生成中间 Long 对象?或者它可能实际上取决于实现getLong()?"
Java 足够聪明,可以保护你不开枪,所以它会创建一个 Long 对象,就像方法签名所请求的那样,然后如果你的代码将它分配给一个 long
它将拆箱。无论出于何种原因开发者必须 return Long
从 getLong()
必须严格遵守。 Boxing/Unboxing 专注于可读性而不是性能。如果您只想在整个代码中使用 long
,则严格要求创建一个新的 long getLong()
方法,这通常会更快。
另外一个问题,你只需要使用调试器,使用调试器提供的"step into"工具,用Eclipse,IntelliJ或者NetBeans试试,命名几个
也许测试您的问题的最简单方法是编写一个小示例,然后 运行 Java 反汇编程序。
假设我们有这个 class:
public class Unboxing {
public static void main(String[] args) {
long l = getLong();
}
public static Long getLong() {
return 10L;
}
}
然后我们可以编译它 (javac Unboxing.java
),编译后我们可以反汇编它 (javap -c -s Unboxing
) 以查看它的字节码并了解 JVM 在幕后做了什么。
public class Unboxing {
public Unboxing();
descriptor: ()V
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
Code:
0: invokestatic #7 // Method getLong:()Ljava/lang/Long;
3: invokevirtual #13 // Method java/lang/Long.longValue:()J
6: lstore_1
7: return
public static java.lang.Long getLong();
descriptor: ()Ljava/lang/Long;
Code:
0: ldc2_w #19 // long 10l
3: invokestatic #21 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
6: areturn
}
在代码中可以看到它获取了一个Long
对象,然后在上面调用它的longValue
方法:
0: invokestatic #7 // Method getLong:()Ljava/lang/Long;
3: invokevirtual #13 // Method java/lang/Long.longValue:()J
之后将其存入变量l
(lstore_1
).
那么,这可能回答了你的问题。
很可能你没有必要也没有理由关心。
是的,正如其他人所说,当一个方法 return 是一个 Long
对象时,它 return 就是一个 Long
对象。或 null
,但这里似乎并非如此。但是,如果该对象是在 return 之前的方法中创建的,那么它是一个非常短暂的对象。垃圾收集器的处理效率如此之高,因此 space 的分配不太可能对您造成伤害。您最终不会消耗大量内存,因为您一次只有一 (1) 个 Long
对象。
在 Edwin Dalorzo 的回答和许多其他情况下,Long
对象甚至根本不会被分配和垃圾收集,并且不需要比原始 long
更多的内存。怎么会? Long
范围从 -127 到 128 的对象已预先分配和共享。因此,当 Edwin 的方法 returns 10L
时,实际上只是 returned 对预分配对象的引用。 return.
没有分配
就是说,如果方法从不 return null
,出于可读性原因 returning 一个对象是一个糟糕的设计。它应该 return 一个原语。如果在某些情况下 return 没有长值,可以考虑 returning 一个 OptionalLong
。这些不是预先分配的,但也是短暂的,因此由垃圾收集器有效处理。
给定以下方法:
Long getLong() {
...
}
如果我调用它并将 return 值分配给这样的变量:
long abc = getLong();
是否会生成一个 Long
对象然后将其转换为 long 或 Java 是否足够聪明以避免生成中间 Long
对象?或者它实际上可能取决于 getLong()
?
我问这个的原因是包装器对象的大小通常比相应的原始类型大小大得多。如果我必须多次调用此方法并且每次都需要为 Long
对象分配内存,程序最终会消耗比实际需要更多的内存,从而触发更多的 GC 周期。
此外,我如何验证执行 long abc = getLong()
时发生的确切步骤(基本上也在寻找有关如何自己获得上述问题答案的指导)?
您问:“会生成一个 Long 对象然后将其转换为 long 还是 Java 足够聪明以避免生成中间 Long 对象?或者它可能实际上取决于实现getLong()?"
Java 足够聪明,可以保护你不开枪,所以它会创建一个 Long 对象,就像方法签名所请求的那样,然后如果你的代码将它分配给一个 long
它将拆箱。无论出于何种原因开发者必须 return Long
从 getLong()
必须严格遵守。 Boxing/Unboxing 专注于可读性而不是性能。如果您只想在整个代码中使用 long
,则严格要求创建一个新的 long getLong()
方法,这通常会更快。
另外一个问题,你只需要使用调试器,使用调试器提供的"step into"工具,用Eclipse,IntelliJ或者NetBeans试试,命名几个
也许测试您的问题的最简单方法是编写一个小示例,然后 运行 Java 反汇编程序。
假设我们有这个 class:
public class Unboxing {
public static void main(String[] args) {
long l = getLong();
}
public static Long getLong() {
return 10L;
}
}
然后我们可以编译它 (javac Unboxing.java
),编译后我们可以反汇编它 (javap -c -s Unboxing
) 以查看它的字节码并了解 JVM 在幕后做了什么。
public class Unboxing {
public Unboxing();
descriptor: ()V
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
Code:
0: invokestatic #7 // Method getLong:()Ljava/lang/Long;
3: invokevirtual #13 // Method java/lang/Long.longValue:()J
6: lstore_1
7: return
public static java.lang.Long getLong();
descriptor: ()Ljava/lang/Long;
Code:
0: ldc2_w #19 // long 10l
3: invokestatic #21 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
6: areturn
}
在代码中可以看到它获取了一个Long
对象,然后在上面调用它的longValue
方法:
0: invokestatic #7 // Method getLong:()Ljava/lang/Long;
3: invokevirtual #13 // Method java/lang/Long.longValue:()J
之后将其存入变量l
(lstore_1
).
那么,这可能回答了你的问题。
很可能你没有必要也没有理由关心。
是的,正如其他人所说,当一个方法 return 是一个 Long
对象时,它 return 就是一个 Long
对象。或 null
,但这里似乎并非如此。但是,如果该对象是在 return 之前的方法中创建的,那么它是一个非常短暂的对象。垃圾收集器的处理效率如此之高,因此 space 的分配不太可能对您造成伤害。您最终不会消耗大量内存,因为您一次只有一 (1) 个 Long
对象。
在 Edwin Dalorzo 的回答和许多其他情况下,Long
对象甚至根本不会被分配和垃圾收集,并且不需要比原始 long
更多的内存。怎么会? Long
范围从 -127 到 128 的对象已预先分配和共享。因此,当 Edwin 的方法 returns 10L
时,实际上只是 returned 对预分配对象的引用。 return.
就是说,如果方法从不 return null
,出于可读性原因 returning 一个对象是一个糟糕的设计。它应该 return 一个原语。如果在某些情况下 return 没有长值,可以考虑 returning 一个 OptionalLong
。这些不是预先分配的,但也是短暂的,因此由垃圾收集器有效处理。