五分钟理解Java中字符串(String)的存储原理

写这篇博客的目的起源于自己想要知道String提供的intern()方法的作用以及String内部的存储原理是怎样的?

如下代码:

String s1 = new String("cc");

s1.intern();

String s2 = new StringBuffer().append("cc").toString();

System.out.println(s2.intern() == s1);

运行环境是jdk1.8, 上面这段代码的执行结果为false,可是为什么是false呢?

一般我们在java当中创建String有两种方式:

1 直接使用双引号声明出来(这种也叫字面量):比如 String str = “aa”。

2 通过new String("aa")或者其他的比如StringBuffer或StringBuilder等。

在接下去之前,先来了解一下基本概念:

class文件里的常量池:一般我们在写java文件的时候,如果定义了"a"这种字符串,就会被编译器解析到class文件的常量池中,比如下面的代码

public void test() {

    String s1 = "aa";

}

用javaap反编译一下,可以看到,编译器将字符串字面量解析到了常量池中。


方法里的字节码在运行时使用ldc加载常量池里的字符串对象ref放到栈顶,然后用astore_1命令将栈顶ref赋值给局部变量s1。


运行时常量池(Constant pool):我们知道每个class文件在jvm运行时会加载到虚拟机中,虚拟机中的每个class文件都会有一个自己的常量池,保存着class文件里的常量信息,比如class全限定名,父类的名字等。

String常量池(String Constant Pool):运行时常量池(Constant pool)里包含了String 常量池,底层实现是一个hashTable。也就是说我们的class文件里使用 " " 的声明的字符串字面量,最终都会放到String常量池

在jdk1.6的时候,String常量池是放在PermGen区,就是我们熟悉的永久代,由于PermGen是固定的大小的,而且不易扩展,当存储的String对象太多,会引发OutOfMemoryException,到了jdk1.7 jvm团队将String常量池移动到了Heap中,并提供了通过命令行控制String常量池的大小。详情可以看https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6962931https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6962930

放张图片方便理解:


String的intern方法作用:

如果字符串在String pool中不存在则创建一个,并返回该字符串的引用,如果在Heap有字符串对象,则返回将引用指向Heap中的对象,然后返回引用。

现在来解释一下代码:

String s1 = "Cat";  //编译期会在class文件常量池中,运行时会放到String常量池(String pool)。

String s2 = "Cat"//同上,运行时如果String pool中已经存在Cat了,就将Cat的引用返回给s2.

String s3 = new String("Cat"); //会在Heap中创建一个String对象Cat,如果Cat不存在于String pool中,也会在String pool中创建(注意不存在创建两个对象),因为上面已经创建了,所以这里只创建一次。

s1 == s2 //true   很好解释,都是同一个对象在String Pool

s1 == s3 //false  由于s3的String对象在Heap中,和s1不是一个对象,== 比较比的是对象的地址,所以不相等。

回答一下开始问题

String s1 = new String("cc"); //Heap创建了一个String对象,同时StringPool里创建了"cc"字符串 

s1.intern(); //这里没有接收返回结果。只是把“cc”写入到StringPool,上一步StringPool里已经有"cc"了

String s2 = new StringBuffer().append("cc").toString(); // 这里只在Heap创建对象。String常量池有了"cc"

System.out.println(s2.intern() == s1); // 不可能相等,s1 是指向Heap里的String 对象,s2.intern()方法返回的是StringPool里“cc”的引用地址。

那么String的intern()方法,这个方法怎么用呢?

String s1 = "aa";

String s2 = new String("aa");

String s3 = s2.intern();

System.out.println(s3 == s1); // 执行的结果为true

这里intern方法的作用是返回String pool中 “aa”这个字符串字面量的引用(即使s2是通过new String在堆中生成对象,但是aa本质还是在String pool中),我们用s3保存这个引用。对比上面的图片,应该能理解执行的结果。s3和s1他们指向的就是String pool中"aa", 而s2指向堆中的String对象

ok,再来看这段代码 s1 == s2 为什么会返回true呢?

String s1 = new String("1") + new String("1"); // 堆中创建"11"字符串对象,将引用给s1,同时String pool创建"1"这个字符串对象

s1.intern();  由于Heap中已经存在一个, String pool 里的"11" 直接指向Heap中的对象了。

String s2 = "11"; // 返回String pool指向的那个对象。

System.out.println(s1 == s2); // 都是指向Heap中的对象,所以相等

总结:对于intern()这个方法,分为两种情况:

1 如果在调用intern()这个方法之前,String pool 里已经有对应的字符串了(一般就是我们直接写在java文件里的“cc”这样的字符串,在加载类的时候,虚拟机会帮我们把字面量生成String对象,存储在String pool 里),那么intern()直接返回String pool 里的String对象。

2 如果在调用intern()这个方法之前,String pool里没有对应字符串,而我们又是通过new String这种方式创建的(例如上面的new String("1") + new String("1"))。那么intern()会把"11"这个String对象(在堆上),放到String pool 里。后面的代码如果有 String s2 = "11";这种,那么s2就指向"11"这个堆上的String对象了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342