Java中的对象生命周期和内存管理?

10
在程序中,对于下面的语句,在堆内存和字符串常量池中会创建多少个对象?
我需要了解对象创建的详细信息。许多我读过的资料没有详细说明。当对象被销毁时我感到困惑。
String a="MAM"+"BCD"+"EFG"+"GFE";

会创建多少个对象?

我正在寻找关于对象、方法和类的生命周期以及当它们被动态更改和修改时JVM如何处理它们的好资料。


有人应该在上面运行javah。我敢打赌它只会显示一个常量。 - Rekin
到我这里只有一个,但是在这个讨论中有很多朋友让我感到困惑。 链接 - saikiran
6个回答

20

"MAM"+"BCD"+"EFG"+"GFE"是一个编译时常量表达式,并编译成"MAMBCDEFGGFE"字符串字面值。JVM将在加载包含上述代码的类时从此字面值创建一个String实例,并将此String放入字符串池中。因此,String a = "MAM"+"BCD"+"EFG"+"GFE";不会创建任何对象,请参见JLS 15.18.1. String Concatenation Operator +

如果表达式是编译时常量表达式(§15.28),则会新创建(§12.5)字符串对象。

它只是将对池中String对象的引用分配给局部变量a


6
只创建一个对象。
string s1 = "java";
string s2 = "ja" + "va";
s.o.p(s1==s2);

该语句的结果为真。
String s1="java";
string s2 = "ja";
String s3 = s2 +"va";
s.o.p(s1==s3);

该语句返回false。

因此,至少一个明显的字符串应该是永久的,然后“+”运算符会生成新的字符串对象(在使用new()时不在常量池中)。所以,你所问的问题并没有一个字符串是永久的。这意味着它只创建一个对象。


3

如果还不存在对象,则创建一个对象并将其放置在常量池中,否则使用现有的对象。编译器将字符串常量按照JLS 3.10.5和15.28规定进行连接。

长字符串字面值总是可以拆分为更短的部分,并作为使用字符串连接运算符+的表达式(可能带括号)编写

http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5


3
大多数答案似乎集中于以下两点:a)完整表达式是一个编译时常量;b)该行本身并不构造一个新对象,而只是引用了一个对象。
然而,迄今为止还没有人提到,String 本身包含对内部 char[] 的引用(它也在常量池中)。
总结:常量池中有两个对象(Stringchar[])。该行既不创建也不销毁任何对象。
关于以下内容:
“我对对象何时被销毁感到困惑。”
由于常量池中的内容只有在类本身被卸载时才会被销毁,因此不会销毁任何对象。最多可以说,引用 a 最终会超出作用域。

2

由于String a会编译成"MAMBCDEFGGFE",因此只会创建一个对象。


0

在您的示例中,指出单个堆对象的答案是正确的。但请考虑以下代码:

public class Tester
{
   public String a="MAM";
   public String b ="BCD";
   public String c = "EFG";
   public String d ="GFE";

   public Tester()
   {
      String abcd = a + b + c + d;
   }
}

在这个例子中,创建了7个字符串。a、b、c和d不会被编译成单个常量 - 它们是成员。然后为每个+运算符创建1个字符串 - 从语义上讲,+是一个连接,但在逻辑上它在内存中创建了一个新的字符串。前两个操作符字符串立即被丢弃,并且现在有资格进行垃圾回收,但内存仍然会变化。
从技术上讲,这里有第8个对象。Tester的实例。
编辑:在评论中已经证明这是无稽之谈。

3
javac编译器有一项优化:并非每次使用+都会生成一个新的字符串,而是会创建一个内部StringBuilder,将abcc连接到一起,然后创建一个新的String。这个优化已经存在十多年了。因此,计算结果为5个String和5个相关的char[],1个StringBuilder以及至少一个相关的char[] - 由于动态调整大小而可能有更多。 - A.H.
你的意思是它会先将a+b连接起来,然后将a+b作为一个字符串附加到c中吗?我没听懂你的意思。 - saikiran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接