为什么在 java 中,当最终结果是 interned 对象的不可变副本时,编译器不优化 new?
Why in java the compiler doesn't optimize new when the end result is an immutable copy of interned object?
我们的行为是 Java 如果使用字符串常量,编译器将使用相同的实例
String a = "abc";
String b = "abc";
//a == b
String c = new String("abc");
// c is a brand new object on the heap;
为什么 java 编译器不优化新字符串并将其替换为等效的赋值?是有一些深刻的设计决定还是只是巧合?我们是否可以期望不同的 JVM 或编译器更积极,并实际用众所周知的静态对象替换不可变对象的堆实例?虽然 String 是最臭名昭著的例子,但我们可以对 Integer 有相同的行为,例如。
String
s 与其他对象有点不同,因为它们被广泛使用并且通常充当 "native types"(例如 int
、float
、...)但实际上是数组(即不是固定的内存大小)。使用内存一遍又一遍地存储相同的内容可能会导致进程在相同的内容上浪费内存(我以前也发生过这种情况)。引入字符串实习是为了让开发人员免于编写自己的 String
池的麻烦。
编译器interns String
constants automatically. Integer
s can have the same mechanism but you need to explicitely call it through Integer.valueOf(int)
.
在你的例子中,转述@BenjaminUrquhart,你明确告诉它通过调用new
创建一个新实例,new
是需要 来创建一个新实例。在某些情况下需要这样做,例如当您调用 obj.clone()
时,您期望的是一个新对象,而不是对 obj
.
的新引用
请注意,在 clone()
的情况下,return 新实例听起来不是强制性的,而是 "general intent"(引用 Javadoc):
[clone()
] Creates and returns a copy of this object. The precise meaning of
"copy" may depend on the class of the object. The general intent is
that, for any object x, the expression:
x.clone() != x
will be true, (...)
严格来说,在那种情况下,您 可以 return 相同的实例,但它不被视为 "good practice" (或至少,不是预期的东西)。
我想这可能与数组浅拷贝有关,其中数组本身是一个不同的实例,但每个对象都是对原始数组实例中对象的引用(参见 JLS §10.7),因此 return 副本不是原始对象的完全独立副本。
首先,String(String)
"copy" 构造函数源于最初的日子,是一个异常。可能是因为 String.intern()
和常量 "..."
做了一些复制预防。永远不需要它,因为 String 是不可变的 final
class.
对于 Integer
有 Integer.valueOf(int)
使用瞬间缓存,默认情况下保存 -128 到 127。
尽管有非常称职的编译器开发团队参与,但 java 字节码编译器编译起来非常幼稚。但是,在字节码到机器码的过程中,可能会发生一些美好的事情。例如对象不是在堆上创建的,而是在堆栈上创建的。
简单的编译至少不太可能包含智能技巧的数据流分析中的错误。 (这也为良好的代码风格提供了一个很好的理由。)
一个例子:
List<String> list = ...
String[] array1 = list.toArray(new String[0]);
String[] array2 = list.toArray(new String[list.size()]);
toArray
需要一个实际的数组实例,因为类型擦除 List list
不再知道它包含 String
s.
从历史上看,作为优化,可以传递一组拟合大小(此处为 list.size()
的版本),然后将其返回。更优化和更快,还有一些样式检查器标记第一个版本。但是实际上第一个版本更快,因为使用了另一个数组字节 cdoe 实例化,并且 array1 的生成速度会稍微快一些。
关于除以一些数字的相同故事。在 C 中,有许多涉及更快转换的编译器优化。这是(部分)在 Java 字节码到机器码编译中完成的,这是这些优化更合乎逻辑的地方。
我个人认为优化字节码编译器会很好,也许可以用于大学项目。然而,仅仅为了代码改进可能是不合理的,比如不使用 .equals
作为枚举值。
我们的行为是 Java 如果使用字符串常量,编译器将使用相同的实例
String a = "abc";
String b = "abc";
//a == b
String c = new String("abc");
// c is a brand new object on the heap;
为什么 java 编译器不优化新字符串并将其替换为等效的赋值?是有一些深刻的设计决定还是只是巧合?我们是否可以期望不同的 JVM 或编译器更积极,并实际用众所周知的静态对象替换不可变对象的堆实例?虽然 String 是最臭名昭著的例子,但我们可以对 Integer 有相同的行为,例如。
String
s 与其他对象有点不同,因为它们被广泛使用并且通常充当 "native types"(例如 int
、float
、...)但实际上是数组(即不是固定的内存大小)。使用内存一遍又一遍地存储相同的内容可能会导致进程在相同的内容上浪费内存(我以前也发生过这种情况)。引入字符串实习是为了让开发人员免于编写自己的 String
池的麻烦。
编译器interns String
constants automatically. Integer
s can have the same mechanism but you need to explicitely call it through Integer.valueOf(int)
.
在你的例子中,转述@BenjaminUrquhart,你明确告诉它通过调用new
创建一个新实例,new
是需要 来创建一个新实例。在某些情况下需要这样做,例如当您调用 obj.clone()
时,您期望的是一个新对象,而不是对 obj
.
请注意,在 clone()
的情况下,return 新实例听起来不是强制性的,而是 "general intent"(引用 Javadoc):
[
clone()
] Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object. The general intent is that, for any object x, the expression:x.clone() != x
will be true, (...)
严格来说,在那种情况下,您 可以 return 相同的实例,但它不被视为 "good practice" (或至少,不是预期的东西)。
我想这可能与数组浅拷贝有关,其中数组本身是一个不同的实例,但每个对象都是对原始数组实例中对象的引用(参见 JLS §10.7),因此 return 副本不是原始对象的完全独立副本。
首先,String(String)
"copy" 构造函数源于最初的日子,是一个异常。可能是因为 String.intern()
和常量 "..."
做了一些复制预防。永远不需要它,因为 String 是不可变的 final
class.
对于 Integer
有 Integer.valueOf(int)
使用瞬间缓存,默认情况下保存 -128 到 127。
尽管有非常称职的编译器开发团队参与,但 java 字节码编译器编译起来非常幼稚。但是,在字节码到机器码的过程中,可能会发生一些美好的事情。例如对象不是在堆上创建的,而是在堆栈上创建的。
简单的编译至少不太可能包含智能技巧的数据流分析中的错误。 (这也为良好的代码风格提供了一个很好的理由。)
一个例子:
List<String> list = ...
String[] array1 = list.toArray(new String[0]);
String[] array2 = list.toArray(new String[list.size()]);
toArray
需要一个实际的数组实例,因为类型擦除 List list
不再知道它包含 String
s.
从历史上看,作为优化,可以传递一组拟合大小(此处为 list.size()
的版本),然后将其返回。更优化和更快,还有一些样式检查器标记第一个版本。但是实际上第一个版本更快,因为使用了另一个数组字节 cdoe 实例化,并且 array1 的生成速度会稍微快一些。
关于除以一些数字的相同故事。在 C 中,有许多涉及更快转换的编译器优化。这是(部分)在 Java 字节码到机器码编译中完成的,这是这些优化更合乎逻辑的地方。
我个人认为优化字节码编译器会很好,也许可以用于大学项目。然而,仅仅为了代码改进可能是不合理的,比如不使用 .equals
作为枚举值。