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();
}

我问是因为,在执行时,两者都呈现相同的结果,如果

我无法得到

更一般地说,任何关于观察 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个数组实例:

  1. 在调用 a.getBytes().length 期间创建的用于测量尺寸(为简单起见未在图中显示)。
  2. new byte[...] 创建的那个尺寸的空的(蓝色的)。
  3. 第二次调用 a.getBytes() 时创建的那个(绿色那个)。