Java 虚拟机处理数组
Array handling by Java Virtual Machine
我有一个愚蠢的问题要问大家。给定以下 Java 代码
public void funct(String a) {
byte[] bytearr;
bytearr = new byte[a.getBytes().length];
bytearr = a.getBytes();
}
新电话有什么变化吗?特别是代码处理方式是否与
不同
public void funct(String a) {
byte[] bytearr;
bytearr = a.getBytes();
}
我问是因为,在执行时,两者都呈现相同的结果,如果
我无法得到
- 它们是不同的(在第一种情况下 space 被分配和填充,而在第二种情况下它更像是一个 "pointer assignation")。
- 他们是一样的,我想太多了。
- 第二个错误,但 JVM 比我聪明并修复了它。
更一般地说,任何关于观察 JVM 背后的内存 allocations/magic 行为的建议都将不胜感激。
谢谢!
它们是不同的,让我们看一下字节码:
1.版本:
L0
LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ARRAYLENGTH
NEWARRAY T_BYTE
ASTORE 1
L1
LINENUMBER 10 L1
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ASTORE 1
L2
LINENUMBER 11 L2
RETURN
L3
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE bytearr [B L1 L3 1
MAXSTACK = 2
MAXLOCALS = 2
2。版本:
L0
LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ASTORE 1
L1
LINENUMBER 10 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
LOCALVARIABLE bytearr [B L1 L2 1
MAXSTACK = 2
MAXLOCALS = 2
区别在于那些来自额外行的指令:
LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ARRAYLENGTH
NEWARRAY T_BYTE
ASTORE 1
这会引入额外的开销(getBytes
被调用两次,指令更多)。通过调用 bytearr = a.getBytes()
jvm 已经处理了存储数组的大小。
但是由于第一次初始化是多余的,编译器可能会在某个时候(经过足够的运行之后)对其进行优化。不过,不需要额外的说明和可读性较差的代码。
唯一的区别是您在第一个片段中创建了一个额外的、不必要的对象。如果你想证明它,你可以在第一个指向第二个数组引用,打印出它们的等价性,然后在第一个数组上调用 string.getBytes() 并再次测试它们的等价性:
String aString = new String("something");
byte[] byteArray1 = new byte[aString.length()];
byte[] byteArray2 = byteArray1;
System.out.println(byteArray1==byteArray2);
byteArray1 = aString.getBytes();
System.out.println(byteArray1==byteArray2);
它在 aString.getBytes() 之前打印出 true,因为引用指向同一个对象。然后aString.getBytes() returns一个新的byte[]数组,所以byteArray1不再== byteArray2。这是回答问题还是您在寻找其他东西?
两个代码块的最终结果是一样的。第一个版本做了一些不必要的额外工作。
版本 1
让我们逐步了解第一个版本。
步骤 1)
bytearr = new byte[a.getBytes().length];
创建了一个新的空数组,bytearr
指向它。
步骤 2)
bytearr = a.getBytes();
a.getBytes()
创建第二个数组。 bytearr
已更新为指向该新数组。旧的(蓝色)数组现在是垃圾,因为不再保留对它的引用。 JVM的垃圾收集器最终会释放它占用的内存。
版本 2
bytearr = a.getBytes();
bytearr
直接指向a.getBytes()
创建的数组。最终结果是一样的,但是这个版本效率更高。
有关版本 1 的更多详细信息
实际上版本 1 在调用期间甚至创建了一个数组 a.getBytes().length
。为了简单起见,最初的解释跳过了它。
所以实际上版本1涉及3个数组实例:
- 在调用
a.getBytes().length
期间创建的用于测量尺寸(为简单起见未在图中显示)。
new byte[...]
创建的那个尺寸的空的(蓝色的)。
- 第二次调用
a.getBytes()
时创建的那个(绿色那个)。
我有一个愚蠢的问题要问大家。给定以下 Java 代码
public void funct(String a) {
byte[] bytearr;
bytearr = new byte[a.getBytes().length];
bytearr = a.getBytes();
}
新电话有什么变化吗?特别是代码处理方式是否与
不同public void funct(String a) {
byte[] bytearr;
bytearr = a.getBytes();
}
我问是因为,在执行时,两者都呈现相同的结果,如果
我无法得到- 它们是不同的(在第一种情况下 space 被分配和填充,而在第二种情况下它更像是一个 "pointer assignation")。
- 他们是一样的,我想太多了。
- 第二个错误,但 JVM 比我聪明并修复了它。
更一般地说,任何关于观察 JVM 背后的内存 allocations/magic 行为的建议都将不胜感激。
谢谢!
它们是不同的,让我们看一下字节码:
1.版本:
L0
LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ARRAYLENGTH
NEWARRAY T_BYTE
ASTORE 1
L1
LINENUMBER 10 L1
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ASTORE 1
L2
LINENUMBER 11 L2
RETURN
L3
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE bytearr [B L1 L3 1
MAXSTACK = 2
MAXLOCALS = 2
2。版本:
L0
LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ASTORE 1
L1
LINENUMBER 10 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
LOCALVARIABLE bytearr [B L1 L2 1
MAXSTACK = 2
MAXLOCALS = 2
区别在于那些来自额外行的指令:
LINENUMBER 9 L0
ALOAD 0
ICONST_0
AALOAD
INVOKEVIRTUAL java/lang/String.getBytes ()[B
ARRAYLENGTH
NEWARRAY T_BYTE
ASTORE 1
这会引入额外的开销(getBytes
被调用两次,指令更多)。通过调用 bytearr = a.getBytes()
jvm 已经处理了存储数组的大小。
但是由于第一次初始化是多余的,编译器可能会在某个时候(经过足够的运行之后)对其进行优化。不过,不需要额外的说明和可读性较差的代码。
唯一的区别是您在第一个片段中创建了一个额外的、不必要的对象。如果你想证明它,你可以在第一个指向第二个数组引用,打印出它们的等价性,然后在第一个数组上调用 string.getBytes() 并再次测试它们的等价性:
String aString = new String("something");
byte[] byteArray1 = new byte[aString.length()];
byte[] byteArray2 = byteArray1;
System.out.println(byteArray1==byteArray2);
byteArray1 = aString.getBytes();
System.out.println(byteArray1==byteArray2);
它在 aString.getBytes() 之前打印出 true,因为引用指向同一个对象。然后aString.getBytes() returns一个新的byte[]数组,所以byteArray1不再== byteArray2。这是回答问题还是您在寻找其他东西?
两个代码块的最终结果是一样的。第一个版本做了一些不必要的额外工作。
版本 1
让我们逐步了解第一个版本。
步骤 1)
bytearr = new byte[a.getBytes().length];
创建了一个新的空数组,bytearr
指向它。
步骤 2)
bytearr = a.getBytes();
a.getBytes()
创建第二个数组。 bytearr
已更新为指向该新数组。旧的(蓝色)数组现在是垃圾,因为不再保留对它的引用。 JVM的垃圾收集器最终会释放它占用的内存。
版本 2
bytearr = a.getBytes();
bytearr
直接指向a.getBytes()
创建的数组。最终结果是一样的,但是这个版本效率更高。
有关版本 1 的更多详细信息
实际上版本 1 在调用期间甚至创建了一个数组 a.getBytes().length
。为了简单起见,最初的解释跳过了它。
所以实际上版本1涉及3个数组实例:
- 在调用
a.getBytes().length
期间创建的用于测量尺寸(为简单起见未在图中显示)。 new byte[...]
创建的那个尺寸的空的(蓝色的)。- 第二次调用
a.getBytes()
时创建的那个(绿色那个)。