Java String创建问题-Android面试准备2019-2-3

本文源自https://www.zhihu.com/question/29884421,由原作者回答整理而成,感谢。

String类

String的定义

String类是一个final类(不可被继承),内部通过一个final字符数组表示该字符串内容。一旦String对象被创建后,就不能再修改此对象中存储的字符串内容。String对象的所有编辑功能都是通过创建一个新的对象来实现的,而不是对原有对象进行修改。

它的equals()方法是通过比较字符数组的内容是否相等来判断字符串对象是否相等。

这里顺带说一下toString()方法:System.out.println()方法会调用打印对象的toString()方法。Object基类默认的toString()方法返回的是getClass().getName() +"@"+ hashcode(),而String类重写了Object基类的toString()方法,来返回String的字面量。

String赋值方式

String有两种赋值方式,第一种是通过"字面量"赋值,比如String str = "Hello"。第二种是通过new关键字创建新对象,比如String str = new String("Hello")。

这两种方式到底有什么不同?程序执行的时候,内存里到底有几个"实例"?"实例"存在了内存的哪里?"字面量"又存在了哪里?"变量"又存在了哪里?

内存回顾

讲之前,先回顾一下内存。

虚拟机结构图

我们主要看运行时数据区,一般讲起来虚拟机內存最主要的就是三块:堆和栈和方法区。

将这三块放大并分析:

(下图中的非堆(Non Heap)可以粗略理解为代表着与堆区分开来的一块区域,包括代码缓冲和永生带,永生带中包括了驻留字符串区(即我们要重点讲的字符串常量池)和方法区,也就是说这里我们将方法区归在非堆中。)

堆与栈与非堆

上图中,首先堆分成新生代和老年代,先不用管它,这是GC垃圾回收时候的事。

再说栈,重要的是栈里的局部变量表(Local Variables)和操作数栈(Operand Stack)。栈是线程私有的,每个线程里的每个方法被执行的时候都会创建一个栈帧(Stack Frame),而每个栈帧里对应的都维护着一个局部变量表和操作数栈。我们总说基本型和对象引用存在栈里,其实就是存在局部变量表里。而操作数栈是线程实际的操作台。看下面这张图,做个加法100+98,局部变量表就是存数据的地方,一直不变,到加法做完再把和加进去。操作数栈就很忙了,先把两个数字压进去,再求和,算出来以后再弹出去。

局部变量表与操作数栈

非堆中的方法区

我们再说方法区。"类"被加载后的信息、常量、静态变量存放在方法区,方法区全局共享。在Hotspot里方法区也叫"永生代",但两者不能等同。

上面说了,每个类加载完之后,数据都存在方法区里。和String最相关的是方法区中的类数据中的运行时常量池(Run-Time Constant pool),它是每个类私有的。后面会说到,每个class文件里的class文件常量池在类被加载器加载之后,就映射存放在这个地方。另外一个是字符串常量池,它和运行时常量池不是—个概念,字符串常量池是全局共享的。位置就在第二张图里Interned Strings(驻留字符串区)的位置,可以理解为在永生代里,方法区外面。后面会讲到,String.intern()方法,字符串驻留之后,字符串的引用就放在这个String Pool。

String创建分析

示例

编译成Test.class文件之后,如下图,除了版本、字段、方法、接口等描述信息外,还有一个也叫常量池的东西(淡绿色区块,即前面提到的class文件常量池),但这个常量池和內存里的常量池不是一个东西。class文件里的常量池主要存两个东西:"字面量"和"符号引用量"。其中字面量就包括类中定义的一些常量,因为String是不可变的(由final关键字修饰过了),所以代码里的"Hello"字符串,就是作为字面量(常量)写在class文件常量池里。

.class文件

运行程序用到Test类的时候,Test.class会被类加载器加载,而Test.class文件的信息就会被解析到内存的方法区里。class文件常量池里大部分数据会被加载到运行时常量池。但String不是,例子中的"Hello"的一个引用会被存到同样在Non Heap区的字符串常量池里,而"Hello"本体还是和所有对象一样,被创建在堆区。测试的结果是在新生代的Eden区,但因为一直有一个引用驻留在字符串常量池,所以不会被GC清理掉。这个 Hello对象会生存到整个线程结束。如下图所示,字符串常量池的具体位置是在过去说的永生代里,方法区的外面。

加载器加载类文件后的内存状况

注意:这只是在Test类被类加载器加载时候的情形。主线程中的str变量这时候都还没有被创建,但Hello的实例已经在堆里了,对它的引用也已经在字符串常量池里了。等主线程开始创建str变量的时候,虛拟机就会到字符串常量池里找,看有没有equals("Hello")为true的String。如果找到了,就在栈区当前栈帧的局部变量表里创建str变量,然后把字符串常量池里对Hello对象的引用复制给str变量。找不到的话,才会在堆重新创建一个对象,然后把引用驻留到字符串常量区,然后再把引用复制到栈帧的局部变量表。

主线程创建str变量后

如果我们定义了很多个值为"Hello"的字符串,比如String str1 = "Hello";String str2 = "Hello";String str3 = "Hello";,有三个变量,也不会在堆上增加String实例,局部变量表里三个变量统一指向同一个堆内存地址。

3个变量的情况

但如果是下面的情况:String str1 = "Hello";String str2 = "Hello";String str3 = new String("Hello");,str3会指向一块由new关键字创建的的新对象。虽然字面还是"Hello",但是是完全不同的对象,有不同的内存地址。

有new关键字的情况

intern()方法

intern()方法让我们能手动检查字符串常量池,把有新字面值的字符串地址驻留到常量池中。

最后补充一下,JDK7开始HotSpot把Interned String从PermGen移到堆,JDK8又彻底取消了PermGen。但不管怎样,基本原来还是不变的。

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