讲一讲Java的字符串常量池,看完你的思路就清晰了

前言

很多朋友Java的字符串常量池的概念困扰了很长一段时间,最近研究了一下jvm指令码,终于对它有了大概的了解。 在展示案例前,我们需要先搞清楚一个概念,众所周知,jvm的内存模型由程序计数器、虚拟机栈、本地方法栈、堆、元空间(方法区)、直接内存组成。 今天我们谈到的概念只和虚拟机栈、堆、元空间(方法区)有关。 先举个例子说明两种关于字符串最基本的使用情况:

  • String s =“abc”;在编译期间,会将等号右边的“abc”常量放在常量池中,在程序运行时,会将s变量压栈,栈中s变量直接指向元空间的字符串常量池abc项,没有经过堆内存。

  • String s = new String(“abc”);在编译期间,会将等号右边的“abc”常量放在常量池中,在程序运行时,先在堆中创建一个String对象,该对象的内容指向常量池的“abc”项。然后将s变量压栈,栈中s变量指向堆中的String对象。

下面通过javap -v xxx.class命令来查看class文件的指令码,通过分析这些指令码更确切的了解我们想知道的问题。笔者的jdk是1.8版本的,版本不同可能效果也不同。

第一个例子:String a = “abc”

首先展示我们的源代码

public class StringCodeTest {
    public static void main(String[] args) {
            String a = "abc";
            System.out.println(a);
        }
   }

我们执行 javap -v StringCodeTest .class>StringCodeTest.txt命令,将指令码放到StringCodeTest.txt中。

Classfile /C:/Users/zhiyi/IdeaProjects/springboottest/target/classes/cn/lizy/service/StringCodeTest.class
  Last modified 2020-11-7; size 610 bytes
  MD5 checksum 2adeba87a0f1b315019efe540bb058cd
  Compiled from "StringCodeTest.java"
public class cn.lizy.service.StringCodeTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#22         // java/lang/Object."<init>":()V
   #2 = String             #23            // abc
   #3 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #26.#27        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #28            // cn/lizy/service/StringCodeTest
   #6 = Class              #29            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcn/lizy/service/StringCodeTest;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               a
  #19 = Utf8               Ljava/lang/String;
  #20 = Utf8               SourceFile
  #21 = Utf8               StringCodeTest.java
  #22 = NameAndType        #7:#8          // "<init>":()V
  #23 = Utf8               abc
  #24 = Class              #30            // java/lang/System
  #25 = NameAndType        #31:#32        // out:Ljava/io/PrintStream;
  #26 = Class              #33            // java/io/PrintStream
  #27 = NameAndType        #34:#35        // println:(Ljava/lang/String;)V
  #28 = Utf8               cn/lizy/service/StringCodeTest
  #29 = Utf8               java/lang/Object
  #30 = Utf8               java/lang/System
  #31 = Utf8               out
  #32 = Utf8               Ljava/io/PrintStream;
  #33 = Utf8               java/io/PrintStream
  #34 = Utf8               println
  #35 = Utf8               (Ljava/lang/String;)V
{
  public cn.lizy.service.StringCodeTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/lizy/service/StringCodeTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: aload_1
         7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        10: return
      LineNumberTable:
        line 5: 0
        line 6: 3
        line 24: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  args   [Ljava/lang/String;
            3       8     1     a   Ljava/lang/String;
}
SourceFile: "StringCodeTest.java"

关键要看Constant pool:(这个就是常量池)和main方法。

main方法中ldc #2 的意思是把常量池中的#2项压入到栈,而#2关联的#23就是“abc”常量。这就说明栈中的对象是直接指向了常量池的。String s = “abc”只生成了一个字符串对象(常量池中的对象)和一个栈中的引用

第二个例子 String s = new String(“abc”);

源代码

public class StringCodeTest {
    public static void main(String[] args) {
            String b = new String("abc");
            System.out.println(b)
        }
   }

为了节省篇幅,第二个例子和第三个例子的jvm指令码就不再展示了,只截图关键点,有需要可以自己通过javap命令生成


main方法中先new #2, 而#2关联的#25就是String对象, 然后再ldc #3(关联的#26就是“abc”常量)也就是说,在执行时,先在堆中创建String对象,再把常量池中的#3项压入到栈(这里的栈应该是操作数栈,参考最上面的内存模型图)供String对象使用。所以,栈中的引用指向了堆中的String对象,String对象指向了常量池中的“abc”项,String s = new String(“abc”)生成了一个字符串对象(常量池中的对象)、一个堆中的String对象和一个栈中的引用。

第三个例子:String c = new String(“abc”)+“abc”;

源代码

public class StringCodeTest {
    public static void main(String[] args) {
            String c = new String("abc")+"abc";
            System.out.println(c);
        }
   }

main方法中先创建了Stringbuiler对象,然后创建了String对象(就是new String("abc")), 再执行ldc #3将常量池中的字符串压入到栈。最后做计算,过程和第二个例子差不多,只不过多了个Stringbuiler的append操作。

总结:理解了大体的原理后,再遇到像 ==判断字符串相等,或者计算String s = new String(“abc”)再内存中创建了几个对象这样的问题时思路就清晰了

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

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