当使用字面量创建字符串时,它存储在池中。但是当使用new操作符创建String对象时,它将对象存储在Heap中。但是堆中的对象仅仅是一个指向存储在池中的文字的指针,还是一个存储在堆中的简单的String对象,适合GC?
68de4m5k1#
术语:
String
String::intern()
你的问题实际上是在谈论字符串池,而不是常量池。回答您的问题:字符串池-字符串总是存在于常量池中吗?不可以。使用new String()创建的字符串对象在字符串池或常量池中都不存在。当使用literal创建string时,它会存储在pool中。字符串(在加载的类文件的常量池中表示)被创建为String对象并添加到字符串池中(这些步骤发生在Java代码首次使用文字之前或使用文字时,具体细节取决于JVM实现)。但是当new操作符用于创建String对象时,它将对象存储在Heap中。是的。但是字符串池也是堆的一部分。就像我说的,它是一个数据结构,而不是一个存储区域。(In过去,字符串池存在于一个特殊的堆中,叫做PermGen堆。但是PermGen被其他东西(MetaSpace)取代了,字符串池不再使用任何一个。但是堆中的对象仅仅是一个指向存储在池中的文字的指针,还是一个存储在堆中的简单的String对象,适合GC?这真的很混乱。所有的字符串在(a)堆中都被表示为String对象。这包括字符串池中的字符串。即使字符串池在PermGen中。所有无法访问的String对象都有资格进行垃圾收集。即使是字符串池中的字符串。即使是表示字符串字面量的String对象。但是...等等...字符串文字可以被垃圾收集吗?是的,我会的!如果一个表示字符串文字的String对象在运行时变得不可访问,它就有资格接受垃圾收集,就像任何其他String对象一样。如果使用字符串文字的代码对象变得不可访问,则字符串文字可能变得不可访问。当类加载器变得不可访问时,可能会发生这种情况,并且发生类卸载。是的,PermGen被垃圾回收了,至少从JDK 1.2开始是这样。(IIRC Java 1.0,也许1.1没有为PermGen堆实现GC。但这在很久以前就被修复了。)
new String()
new
qv7cva1a2#
它是一个存储在堆中的String对象,适合GC。我使用的是Amazon Corretto 17。我发现了令人困惑的解释,所以我决定用代码来调查,通过运行一个测试来比较每种情况下的String对象创建时间:
startTime = System.nanoTime(); String a = "a"; endTime = System.nanoTime(); System.out.println(endTime - startTime); startTime = System.nanoTime(); String b = new String("b"); endTime = System.nanoTime(); System.out.println(endTime - startTime);
字符串在我的机器上运行上述10次,创建String“a”需要2100- 4900 ns,创建String“b”需要4900- 15500 ns,所以创建b必须做一些比创建a更昂贵的事情。然后,我使用String.intern()方法比较了具有相同字符集的String对象的引用,该方法返回字符串池中String对象的引用。
String c = "hello"; String d = new String("hello"); System.out.println(c==d); // false System.out.println(c==c.intern()); // true System.out.println(d==d.intern()); // false System.out.println((c.intern()==d.intern())); // true
型有了这些,我们可以推断出c和d指向不同的对象,c是字符串池中的String对象(从Java8开始,它存储在非堆内存中),d是堆中的String对象。关于GC,我相信只有当字符串池所在的非堆内存达到极限时,它才会被垃圾收集。那个内存似乎被称为Metaspace并取代了PermGen。
2条答案
按热度按时间68de4m5k1#
术语:
String
对象,以及由String::intern()
添加到池中的String
对象)。你的问题实际上是在谈论字符串池,而不是常量池。
回答您的问题:
字符串池-字符串总是存在于常量池中吗?
不可以。使用
new String()
创建的字符串对象在字符串池或常量池中都不存在。当使用literal创建string时,它会存储在pool中。
字符串(在加载的类文件的常量池中表示)被创建为
String
对象并添加到字符串池中(这些步骤发生在Java代码首次使用文字之前或使用文字时,具体细节取决于JVM实现)。但是当
new
操作符用于创建String对象时,它将对象存储在Heap中。是的。但是字符串池也是堆的一部分。就像我说的,它是一个数据结构,而不是一个存储区域。
(In过去,字符串池存在于一个特殊的堆中,叫做PermGen堆。但是PermGen被其他东西(MetaSpace)取代了,字符串池不再使用任何一个。
但是堆中的对象仅仅是一个指向存储在池中的文字的指针,还是一个存储在堆中的简单的String对象,适合GC?
这真的很混乱。
所有的字符串在(a)堆中都被表示为
String
对象。这包括字符串池中的字符串。即使字符串池在PermGen中。所有无法访问的
String
对象都有资格进行垃圾收集。即使是字符串池中的字符串。即使是表示字符串字面量的String
对象。但是...等等...字符串文字可以被垃圾收集吗?
是的,我会的!如果一个表示字符串文字的
String
对象在运行时变得不可访问,它就有资格接受垃圾收集,就像任何其他String
对象一样。如果使用字符串文字的代码对象变得不可访问,则字符串文字可能变得不可访问。当类加载器变得不可访问时,可能会发生这种情况,并且发生类卸载。
是的,PermGen被垃圾回收了,至少从JDK 1.2开始是这样。(IIRC Java 1.0,也许1.1没有为PermGen堆实现GC。但这在很久以前就被修复了。)
qv7cva1a2#
它是一个存储在堆中的String对象,适合GC。我使用的是Amazon Corretto 17。
我发现了令人困惑的解释,所以我决定用代码来调查,通过运行一个测试来比较每种情况下的String对象创建时间:
字符串
在我的机器上运行上述10次,创建String“a”需要2100- 4900 ns,创建String“b”需要4900- 15500 ns,所以创建b必须做一些比创建a更昂贵的事情。
然后,我使用String.intern()方法比较了具有相同字符集的String对象的引用,该方法返回字符串池中String对象的引用。
型
有了这些,我们可以推断出c和d指向不同的对象,c是字符串池中的String对象(从Java8开始,它存储在非堆内存中),d是堆中的String对象。关于GC,我相信只有当字符串池所在的非堆内存达到极限时,它才会被垃圾收集。那个内存似乎被称为Metaspace并取代了PermGen。