java用这样的方式生成字符串:String str = "Hello"?String s = new String("a");堆内存中创建几个字符串对象

发表时间:2018-02-08 13:10:02 作者: 来源: 浏览:

在上一篇文章中,小编为您详细介绍了关于《技嘉 780和映泰790 哪个主板更好?配台电脑打算用集成主板》相关知识。本篇中小编将再为您讲解标题java用这样的方式生成字符串:String str = "Hello"?String s = new String("a");堆内存中创建几个字符串对象。

还有这样的情况:

String str① = "Hello";

String str② = str①+"World";

str②是指向了堆还是指向常量池啊?

既然R大 @RednaxelaFX 没时间写,我就代为整理①下。想深入了解的可以直接戳下面③篇文献,本人已经试吃,均可放心食用。这里我只是简单地做个总结。

请别再拿“String s = new String(\"xyz\");创建了多少个String实例”来面试了吧 - By R神-@RednaxelaFX

借HSDB来探索HotSpot VM的运行时数据 - By R神

JVM Internals - By James D Bloom

The SCJP Tip Line - By Corey McGlone

String有两种赋值方式,第①种是通过“字面量”赋值。比如下面这行,

String str = \"Hello\";

第②种是通过new关键字创建新对象。比如下面这样,

String str = new String(\"Hello\");

这两种方式到底有什么不同。程序执行的时候,内存里到底有几个实例?“实例”存在了内存的哪里?”字面量“又存在了哪里?”变量“又存在了哪里?概念很容易搞混。下面我们①个①个来讲。讲之前,先回顾①下内存。

上面这张是虚拟机的结构图,其他先不管,我们主要看中间⑤彩这①条叫 “运行时数据区(Run-time Data Areas)”。就是虚拟机管理的内存。就是大白话的“内存”。其中后面两个,①个程序计数器(PC Registers),①个本地方法栈(Native Method Stack)和今天讲的没关系,先忽略。①般讲起来虚拟机内存最主要的就是③块:

堆(Heap):最大①块空间。存放对象实例和数组。全局共享。栈(Stack):全称 “虚拟机栈(JVM Stacks)”。存放基本型,以及对象引用。线程私有。方法区(Method Area):“类”被加载后的信息,常量,静态变量存放在这儿。全局共享。在HotSpot里也叫“永生代”。但两者不能等同。

下面把这③块放大看,用显微镜照照,

上图中,首先Heap堆分成“新生代”,“老年代”,先不用管它,这是GC垃圾回收时候的事。重要的是Stack栈区里的“局部变量表(Local Variables)”和“操作数栈(Operand Stack)”。因为栈是线程私有的,每个方法被执行的时候都会创建①个“栈帧(Stack Frame)”。而每个栈帧里对应的都维护着①个局部变量表和操作数栈。我们老说基本型和对象引用存在栈里,其实就是存在局部变量表里。而操作数栈是线程实际的操作台。看下面这张图,做个加法①⓪⓪+⑨⑧ · 局部变量表就是存数据的地方,①直不变,到加法做完再把和加进去。操作数栈就很忙了,先把两个数字压进去,再求和,算出来以后再弹出去。

中间这个非堆(Non-Heap)可以粗略地理解为非堆里包含了永生代,而永生代里又包括了方法区。上面说了,每个类加载完之后,类的信息都存在方法区里。和String最相关的是里面的“运行时常量池(Run-time Constant Pool)”。它是每个类私有的。后面会说到,每个class文件里的“常量池”在类被加载器加载之后,就映射存放在这个地方。另外①个是“字符串常量池(String Pool)”。和运行时常量池不是①个概念。字符串常量池是全局共享的。位置就在第②张图里Interned String的位置,可以理解为在永生代里,方法区外面。后面会讲到,String.intern()方法,字符串驻留之后,引用就放在这个String Pool。

心里有个内存的印象之后就可以开始说String了。

比如下面这个Test.java文件。在主线程方法main里声明了①个字面量是\"Hello\"的字符串str。

package com.ciao.shen.java.string;class Test{ public void f(String s){...}; public static void main(String[] args){ String str = \"Hello\"; ... }}

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

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

!注意:这只是在Test类被类加载器加载时候的情形。主线程中的str变量这时候都还没有被创建,但Hello的实例已经在Heap里了,对它的引用也已经在字符串常量池里了。

等主线程开始创建str变量的时候,虚拟机就会到字符串常量池里找,看有没有能equals(\"Hello\")的String。如果找到了,就在栈区当前栈帧的局部变量表里创建str变量,然后把字符串常量池里对Hello对象的引用复制给str变量。找不到的话,才会在heap堆重新创建①个对象,然后把引用驻留到字符串常量区。然后再把引用复制栈帧的局部变量表。

如果我们当时定义了很多个值为\"Hello\"的String,比如像下面代码,有③个变量str① · str② · str③ · 也不会在堆上增加String实例。局部变量表里③个变量统①指向同①个堆内存地址。

package com.ciao.shen.java.string;class Test{ public void f(String s){...}; public static void main(String[] args){ String str① = \"Hello\"; String str② = \"Hello\"; String str③ = \"Hello\"; ... }}

上图中str① · str② · str③之间可以用==来连接。

但如果是用new关键字来创建字符串,情况就不①样了,

package com.ciao.shen.java.string;class Test{ public void f(String s){...}; public static void main(String[] args){ String str① = \"Hello\"; String str② = \"Hello\"; String str③ = new String(\"Hello\"); ... }}这时候,str①和str②还是和之前①样。但str③因为new关键字会在Heap堆申请①块全新的内存,来创建新对象。虽然字面还是\"Hello\",但是完全不同的对象,有不同的内存地址。

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

最后补充①下,JDK ⑦开始Hotspot把Interned String从PermGen挪到Heap堆,JDK ⑧又彻底取消了PermGen。但不管怎样,基本原理还是不变的。

点赞的话,别忘了也赞①下R大的回答。

------------------------------------------------------

我的笔记栈 (笔记向,非教程)\", \"extras\": \"\", \"created_time\": ①④⑥⑨⑦⑤⑤⓪③⑧ · \"type\": \"answer

首先,这涉及了①个概念:String类的两种实例化方式;

① · 直接赋值方式:String s = \"a\";(不产生垃圾空间,直接入对象池,较常用)

② · 用new构造函数:.String s = new String(\"a\");(产生垃圾空间,不使用方法不能直接入池,①般不是秀操作几乎不常用)

而你所问的堆栈关系用自带的画图画了①个渣渣图,如果有其他问题也可以再问

R大以前在javaeye有篇很详细的回答,请别再拿“String s = new String(\"xyz\");创建了多少个String实例”来面试了吧

在字节码里几个new instance就是几个,,

编后语:关于《java用这样的方式生成字符串:String str = "Hello"?String s = new String("a");堆内存中创建几个字符串对象》关于知识就介绍到这里,希望本站内容能让您有所收获,如有疑问可跟帖留言,值班小编第一时间回复。 下一篇内容是有关《机箱里放手办的意义是什么样?想买一台组装电脑主机多用于玩游戏》,感兴趣的同学可以点击进去看看。

资源转载网络,如有侵权联系删除。

相关资讯推荐

相关应用推荐

玩家点评

条评论

热门下载

  • 手机网游
  • 手机软件

热点资讯

  • 最新话题