java string 字符位置_Java字符串究竟存储在哪里

本文出自——可乐可乐可,作者主页链接:可乐可乐可的个人主页

早上看群友们在讨论,出现了Java字符串究竟在哪里的话题,粗略目睹虚拟机的菜鸡本人起了兴趣,研究了一下整理出来这个博文

字符串究竟存在哪里

这里总结了大家同意的说法:

如果使用常量的方式,该对象将被存储在常量池(永久代)

如果使用new的方式,该对象将被存储在堆

下面的代码揭示了情况:

使用常量的形式,为同一个对象

使用new的方式,为不同的对象

即使调用了常量创建的对象的方法,该对象也并不会发生什么变化:说明就是一个对象,而不是一组数组

public class TestString{

public static void main(String[] args){

String a="Aa";

String b="Aa";

System.out.println(a==b);

String c=new String("Aa");

System.out.println(b==c);

b.charAt(0);

String d=new String("Aa");

System.out.println(d==c);

System.out.println(b==a);

}

}

/**

true

false

false

true

Process finished with exit code 0

**/

复制代码

简单的规则就是这样了,希望浅显了解看到这里就足够了

但反观具体的实现,又会觉得奇妙,比如Java的字符串常量池,intern方法,下面详细说明

更深入一步的了解

在开始之前,各位先看这个String中神奇的方法

String.intem();

/**

* Returns a canonical representation for the string object.

*

* A pool of strings, initially empty, is maintained privately by the

* class String.

*

* When the intern method is invoked, if the pool already contains a

* string equal to this String object as determined by

* the {@link #equals(Object)} method, then the string from the pool is

* returned. Otherwise, this String object is added to the

* pool and a reference to this String object is returned.

*

* It follows that for any two strings s and t,

* s.intern() == t.intern() is true

* if and only if s.equals(t) is true.

*

* All literal strings and string-valued constant expressions are

* interned. String literals are defined in section 3.10.5 of the

* The Java™ Language Specification.

*

* @return a string that has the same contents as this string, but is

* guaranteed to be from a pool of unique strings.

*/

public native String intern();

复制代码

这个方法是一个 native 的方法,但注释写的非常明了。“如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回”。

先记住这个奇妙的方法,建议你思考一下这个逻辑。

我们尝试着使用这个intern方法,并判断intern方法的作用

public static void main(String[] args){

//这里使用常量池存在的字符串,构建字符串对象

String s = new String("1");

s.intern();

String s2 = "1";

System.out.println(s == s2);

//这里构建一个常量池不存在的字符串对象

String s3 = new String("1") + new String("1");

s3.intern();

String s4 = "11";

System.out.println(s3 == s4);

}

复制代码

代码解释:

s与s1的目的是为了比较:当常量池存在这个值时,new方法产生的字符串 与 intern后再使用常量产生的字符串是否为一个对象。

s3与s4的目的是为了比较:当常量池不存在这个值时,new方法产生的字符串 与 intern后再使用常量产生的字符串是否为一个对象。

如果你有两个JDK环境:1.6与1.7

你会得到两个截然不同的答案:

JDK1.6: false false

JDK1.7: false true

然后了解一下JDK1.6与JDK1.7中常量池的区别

先看看JDK1.6

在JDK1.6中,常量池是一块独立的区域,jdk6中的常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的。

有点人可能不是很懂

这里再换种说法:

“String”类型的String,存放在常量池,同时常量池与Java堆是两个不同的部分。

也就是说,

存放在常量池和存放在堆,实现方式都不一样。

常量池默认4M,所以存在消耗尽,抛出java.lang.OutOfMemoryError: PermGen space

调用intern方法,字符串若不存在在常量池,则会在方法区新建一个并返回

ff5771b8dca515f6dd7380fcb569d78e.png

来到JDK1.7

在JDK1.7中,取消了这个称为perm的区域,常量池就是堆,在常量池中的String对象和堆的String对象是存在与同一区域的。

在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了。

为什么要移动,Perm 区域太小是一个主要原因,当然据消息称 jdk8 已经直接取消了 Perm 区域,而新建立了一个元区域。应该是 jdk 开发者认为 Perm 区域已经不适合现在 JAVA 的发展了。

也就是说:JDK1.7中,常量池被放在了堆中,于是我们再次调用一个 常量池不存在的字符串的intern方法,就不需要新建对象了。

9db28a87cd65a6664dd9f41784024934.png

总结:JDK1.7中,字符串的位置从方法区调整到了堆,执行intern时,若没有这个字符,将把当前的字符串放进常量池,而不是新建一个

问题发现

综合上述,上述的代码其实是为了对字符串对象是否转移了区域进行验证

在JDK1.6和JDK1.7中运行同样的代码,结果却会有不同

public static void main(String[] args){

//这里使用常量池存在的字符串,构建字符串对象

String s = new String("1");

s.intern();

String s2 = "1";

System.out.println(s == s2);

//这里构建一个常量池不存在的字符串对象

String s3 = new String("1") + new String("1");

s3.intern();

String s4 = "11";

System.out.println(s3 == s4);

}

复制代码

在JDK1.6中,常量池和堆是明显分隔的

于是我们可以推断:

s1与s2

s对象new出,于是在堆中

s2对象为常量,存在与常量池

s与s2存储的位置都不同,s!=s2

s3与s4

s3为两个字符串对象相加,调用了intern方法

但是存储位置都不同一个在堆里,一个在常量池,s3必然不同于s4

结果为false false

47cb5a687f456cffc01103b7b31420c1.png

在JDK1.7中,由于不存在了perm这个特殊的区域

s对象new出,于是在堆中

s2对象为常量,存在与常量池

s与s2存储的位置都不同,s!=s2

对于s3与s4

s3对象值为“11”,但是是由两个"1"拼接,所以常量池中只会存在"1"而不会存在"11"(这点很重要)

s3intern方法,在常量池中生成了"11"。因为此时常量池中不存在“11”字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个 “11” 的对象,关键点是 jdk7 中常量池不在 Perm 区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。

s4直接使用常量池"11",“11”这个对象其实就是s3

s3==s4

结果为true true

3d8fc46950dcbd9d3dfc8d12e23132d6.png

参考文章

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值