我很好奇。如果我将一些局部变量简单地用作其他变量的别名(也就是说,新的“别名”变量只是赋予其他变量的值而别名'赋值永远不会改变),那么JVM是否通过直接使用原始变量?
比如说,我正在编写一个(不可变的)四元数类(quaternion是一个带有四个值的向量)。我写了一个multiply()
方法,它接受另一个Quaternion
,将两者相乘,然后返回结果Quaternion
。为了节省输入并增加可读性,我在执行实际计算之前创建了几个别名:
public class Quaternion {
private double[] qValues;
public Quaternion(double q0, double q1, double q2, double q3) {
qValues = new double[] {q0, q1, q2, q3};
}
// ...snip...
public Quaternion multiply(Quaternion other) {
double a1 = qValues[0],
b1 = qValues[1],
c1 = qValues[2],
d1 = qValues[3],
a2 = other.qValues[0],
b2 = other.qValues[1],
c2 = other.qValues[2],
d2 = other.qValues[3];
return new Quaternion(
a1*a2 - b1*b2 - c1*c2 - d1*d2,
a1*b2 + b1*a2 + c1*d2 - d1*c2,
a1*c2 - b1*d2 + c1*a2 + d1*b2,
a1*d2 + b1*c2 - c1*b2 + d1*a2
);
}
}
那么,在这种情况下,JVM是否会取消a1
,b1
等,而只是直接使用qValues[n]
和other.qValues[n]
?
答案 0 :(得分:2)
没有像你在Java中描述过的那样的别名。将值从一个内存位置分配给新变量时,JVM会复制该值。如果要创建别名,则在计算期间从另一个线程更改基础数组会改变结果。在您的示例中不会发生这种情况,因为您特意告诉JVM首先复制值。
如果你担心表现,不要。程序正确性胜过所有性能问题。任何产生错误结果的程序都是毫无价值的。我并不是说直接在计算中访问数组必然会产生不正确的结果,因为我还没有看到其余的代码,但是我说这种类型的微优化不值得你努力而不首先找到一个性能问题和下一次执行时序测试以验证问题所在。
答案 1 :(得分:1)
javac编译器不会这样做。像这样拆解一段简单的代码:
int a = 1;
int b = a;
System.out.println("" + (a - b));
节目:
0: iconst_1
1: istore_1
2: iload_1
3: istore_2
...
但这就是解释器将要执行的内容(甚至解释器有时也可以进行一些基本的优化)。 JIT编译器将处理这些类型的优化以及许多其他优化;在你的方法的情况下,它甚至小到内联,所以你甚至不会在JIT启动后获得方法调用开销。
(例如,在我的例子中,JIT可以很容易地进行常量传播,只是取消变量和计算,只需使用"" + 0
作为println()方法的参数进行调用。)
但是,最后,只需遵循JIT黑客总是说的:编写代码以便维护。不要担心JIT会做什么或不做什么。
(注意:大卫对于不是“别名”的变量是正确的,而是原始值的副本。)
答案 2 :(得分:1)
正如其他答案所指出的那样,Java中没有别名变量的概念,并且每个声明的变量都存储变量的值。
使用局部变量存储数组的值以供将来计算是一个更好的主意,因为它使代码更具可读性,并消除了数组的额外读取。
话虽这么说,创建局部变量确实增加了方法线程中分配的java堆栈帧的大小。这在这个特定问题中不是问题,但是更多的局部变量会增加执行所需的堆栈大小。如果涉及递归,这将特别相关。
答案 3 :(得分:-1)
您的计划应该有效,但问题的答案是否定的。 JVM不会将a1
视为qValues[0]
的别名,而是将后者的值复制到前者。