Java 是否优化运行时字符串的创建?
假设我在运行时创建了以下两个字符串(例如,根据用户输入):
public void someMethod(String input) {
if ( input == null ) return;
String a = input + input;
String b = input;
...
}
Java(及其编译器)是否足够智能,可以在运行时检测到 b
包含在 a
因此不需要为b
分配内存? b
可以只指向一半长度的 a
吗?
换句话说,Java 是否实现了 String.intern()
的动态版本?
编辑
考虑到到目前为止所做的答案,我的例子应该是:
public void someMethod(String input) {
if ( input == null ) return;
String a = input + input + input;
String b = input + input;
...
}
Let's assume I create the following two strings at runtime (from user input for example):
public void someMethod(String input) {
if ( input == null ) return;
String a = input + input;
String b = input;
...
}
Is Java (and its compiler) smart enough to detect at runtime that b
is contained in a
and therefore it is not necessary to allocate memory for b
? b
could just point at a
with half the length?
In other words, does Java implement a dynamic version of String.intern()
?
EDIT
Considering answers made so far, my example should be:
public void someMethod(String input) {
if ( input == null ) return;
String a = input + input + input;
String b = input + input;
...
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您实际上并没有在示例中创建两个字符串:
b
只是对input
的引用。因此,为了完成您所要求的操作,Java 必须在创建新字符串时以某种方式返回并更改旧字符串(例如通过说input + input
)。为了回答您更广泛的问题,据我所知,两个字符串共享内存的唯一方法(此外,正如您提到的,是
intern()
'd)是使用substring()
。因此,如果您确实想节省内存,可以这样做:(要明确的是,只有当
b
的值存储在input
之外的其他位置时,这才会节省内存。被丢弃并最终被垃圾收集。)编辑
新的和改进的问题的新的和改进的示例:(
请注意,这总是比问题中的第二个示例节省内存,因为我们完全避免了分配,所以前面的警告。不适用。)
You're not actually creating two strings in your example:
b
is just a reference toinput
. So, to do what you're asking, Java would have to somehow go back and alter old strings when a new string is created (such as by sayinginput + input
).To answer your broader question, AFAIK the only way for two strings to be sharing memory (besides, as you mention, being
intern()
'd) is for one or both to have been created usingsubstring()
. So if you really wanted to save on memory, you could do this:(To be clear, this will only save memory if the value of
b
is being stored somewhere butinput
is discarded and winds up garbage-collected.)EDIT
New and improved example for new and improved question:
(Note that this will always save memory over the second example in the question, since we've avoided an allocation altogether. So the previous caveat doesn't apply.)
不,这不会发生,因为作为参数传入的字符串已经存在,并且字符串是不可变的。双倍长度的数组必须单独创建,因为原始数组没有空间容纳更多字符。
但在相反的情况下,您从长字符串开始,然后使用 substring() 创建一个较短的字符串,这两个字符串实际上将共享相同的 char 数组。
No, this won't happen, because the String passed in as an argument already exists, and Strings are immutable. The double-length one has to be created separately, since the original array won't have room for more characters.
In the reverse case, though -- where you start out with the long string, and use
substring()
to create a shorter one -- the two strings will, indeed, share the same char array.正如其他人指出的那样,
b
只是指向与input
相同的字符串。至于
a
,我们可以使用javap 反汇编代码:编译器使用
StringBuilder.append()
执行串联,(至少在 Sun/OpenJDK 中)只是将所有内容复制到一个大的中char
数组 - 这里没有intern()
ing。这同样适用于您的编辑 - 输入字符串总共被附加 5 次。我可以想象可以编写 String(Builder|Buffer) 的替代实现来维护数组链,这在某些情况下可以实现更好的重用和效率。
As others have pointed out,
b
just points to the same string asinput
.As for
a
, we can use javap to disassemble your code:The compiler uses
StringBuilder.append()
to perform the concatenation, which (in Sun/OpenJDK, at least) just copies everything into a bigchar
array - nointern()
ing here. Same applies with your edit - the input string is appended 5 times in total.I can imagine an alternate implementation of
String(Builder|Buffer)
could be written to maintain a chain of arrays, which would allow better reuse and efficiency in some cases.不,编译器足够智能,可以在编译时检测字符串连接,例如:
但是每次创建字符串时,子字符串匹配的工作量有点大。
然而,Java 试图避免创建多个字符串(如果两个字符串相等),您应该检查
String.intern()
Nop, the compiler is intelligent enough to detect String concatenation at compile time, for instance:
But substring matching is a bit much to do every time you create a string.
However Java tries to avoid multiple string creation (if two strings are equal), you should check
String.intern()