转载自  java 为什么需要常量池

java中讲的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池。

java内存模型中将内存分为堆和栈,其中堆为线程间共享的内存数据区域,栈为线程间私有的内存区域。堆又包括方法区以及非方法区部分,栈包括本地方法栈、虚拟机栈等,如下图所示:

为什么需要常量池

jvm 在栈帧(frame) 中进行操作数和方法的动态链接(link),为了便于链接,jvm 使用常量池来保存跟踪当前类中引用的其他类及其成员变量和成员方法。

每个栈帧(frame)都包含一个运行常量池的引用,这个引用指向当前栈帧需要执行的方法,jvm使用这个引用来进行动态链接。

在 c/c++ 中,编译器将多个编译期编译的文件链接成一个可执行文件或者dll文件,在链接阶段,符号引用被解析为实际地址。java 中这种链接是在程序运行时动态进行的。

常量池探秘

每个 java 文件编译为 class 文件后,都将产生当前类独有的常量池,我们称之为静态常量池。class 文件中的常量池包含两部分:字面值(literal)和符号引用(Symbolic Reference)。其中字面值可以理解为 java 中定义的字符串常量、final 常量等;符号引用指的是一些字符串,这些字符串表示当前类引用的外部类、方法、变量等的引用地址的抽象表示形式,在类被jvm装载并第一次使用这些符号引用时,这些符号引用将会解析为直接引用。符号常量包含:

  • 类和接口的全限定名

  • 字段的名称和描述符

  • 方法的名称和描述符

jvm在进行类装载时,将class文件中常量池部分的常量加载到方法区中,此时方法区中的保存常量的逻辑区域称之为运行时常量区。

使用javap -verbose 命令可以查看class字节码的详细信息,其中包含了编译期确定的静态常量池。

public class StringTest {public static void main(String[] args){String s = new String("abc");String s2 = s.intern();System.out.println(s2 == s);String s3 = (s + s2);System.out.println(s3 == s3.intern());}
}

上述代码javap -verbose后得到(只拿出常量池部分):

major version: 52
Constant pool:#1 = Methodref          #13.#26        // java/lang/Object."<init>":()V#2 = Class              #27            // java/lang/String#3 = String             #28            // abc#4 = Methodref          #2.#29         // java/lang/String."<init>":(Ljava/lang/String;)V#5 = Methodref          #2.#30         // java/lang/String.intern:()Ljava/lang/String;#6 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;#7 = Methodref          #33.#34        // java/io/PrintStream.println:(Z)V#8 = Class              #35            // java/lang/StringBuilder#9 = Methodref          #8.#26         // java/lang/StringBuilder."<init>":()V#10 = Methodref          #8.#36         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#11 = Methodref          #8.#37         // java/lang/StringBuilder.toString:()Ljava/lang/String;#12 = Class              #38            // StringTest#13 = Class              #39            // java/lang/Object#14 = Utf8               <init>#15 = Utf8               ()V#16 = Utf8               Code#17 = Utf8               LineNumberTable#18 = Utf8               main#19 = Utf8               ([Ljava/lang/String;)V#20 = Utf8               StackMapTable#21 = Class              #40            // "[Ljava/lang/String;"#22 = Class              #27            // java/lang/String#23 = Class              #41            // java/io/PrintStream#24 = Utf8               SourceFile#25 = Utf8               StringTest.java#26 = NameAndType        #14:#15        // "<init>":()V#27 = Utf8               java/lang/String#28 = Utf8               abc#29 = NameAndType        #14:#42        // "<init>":(Ljava/lang/String;)V#30 = NameAndType        #43:#44        // intern:()Ljava/lang/String;#31 = Class              #45            // java/lang/System#32 = NameAndType        #46:#47        // out:Ljava/io/PrintStream;#33 = Class              #41            // java/io/PrintStream#34 = NameAndType        #48:#49        // println:(Z)V#35 = Utf8               java/lang/StringBuilder#36 = NameAndType        #50:#51        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#37 = NameAndType        #52:#44        // toString:()Ljava/lang/String;#38 = Utf8               StringTest#39 = Utf8               java/lang/Object#40 = Utf8               [Ljava/lang/String;#41 = Utf8               java/io/PrintStream#42 = Utf8               (Ljava/lang/String;)V#43 = Utf8               intern#44 = Utf8               ()Ljava/lang/String;#45 = Utf8               java/lang/System#46 = Utf8               out#47 = Utf8               Ljava/io/PrintStream;#48 = Utf8               println#49 = Utf8               (Z)V#50 = Utf8               append#51 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;#52 = Utf8               toString

我们可以看到,常量池共包含52个常量。#1 是一个类中方法的符号引用,它由 #13 和 #26 两个utf8编码的字符串构成;#3 是程序中定义的 String 类型的字面值 "abc",它包含指向一个utf8编码字符串 "abc" 的索引 #28

方法的调用、成员变量的访问最终都是通过运行时常量池来查找具体地址的。

String 常量池

运行时常量池有一种 String 类型的常量,即通常我们所说的字符串字面值,所有的字符串字面值组成一个 String 常量表。String常量表并不是一成不变的,程序运行时可以动态添加字符串常量,使用String的intern()可以动态的添加String常量。但

jvm 确保两个在值上完全相等的字符串字面值(即其中包含的字符序列是相同的,使用equals()来判断)指向同一个 String 实例。

如:

String s1 = "abc";String s2 = "abc";System.out.println(s1 == s2); // true

上述代码中的字符串 s1 和 s2 将指向同一个 String 实例。实际上通过查看class文件,我们可以看到,在编译后,静态常量池中已经包含了一个 String 类型的字面值 "abc",程序运行时只是从常量池中获取这个String字面值的引用地址,并赋值给变量 s1 和变量 s2。

Constant pool:#1 = Methodref          #6.#19         // java/lang/Object."<init>":()V#2 = String             #20            // abc······#20 = Utf8               abcpublic static void main(java.lang.String[]);······Code:stack=3, locals=3, args_size=10: ldc           #2                  // String abc2: astore_13: ldc           #2                  // String abc

其中,ldc 表示将一个常量加载到操作数栈。

String 的 intern() 是一个native方法,返回的是一个String对象的标准表示。当调用该方法时,如果运行时常量池中已经存在与之相等(equal())的字符串,则直接返回常量池中的字符串引用,否则将此字符串添加到池中,并返回。

String s1 = "abc";String s2 = new String("abc");System.out.println(s1 == s2);           //返回 falseSystem.out.println(s1.equals(s2));      //返回 trueSystem.out.println(s1 == s2.intern());  //返回 true

上述代码中,虽然 s1 和 s2 中的值是相同的,但是他们指向的并不是同一个对象,但 s2 的标准化表示和s1是同一个 String 对象,都是编译期确定的常量池中的 "abc"。

java 为什么需要常量池 1相关推荐

  1. 好好说说Java中的常量池之Class常量池

    前言 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的Strin ...

  2. Java当中的常量池

    本文转载公众号  达叔与他的朋友们 Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区. 程序计数器是jvm执行程序的流水线,是用来存放一些 ...

  3. 好好说说Java中的常量池之Class常量池 1

    转载自   好好说说Java中的常量池之Class常量池 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考 ...

  4. Java中整数常量池的概念

    Java中整数常量池的概念: java中为了提高程序的执行效率,将[-128, 127]之间256个整数所有的包装对象提前创建好了,类加载时就已经创好了,放在了一个方法区的"整数常量池&qu ...

  5. 第46节:Java当中的常量池

    Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区. 程序计数器是jvm执行程序的流水线,是用来存放一些指令的,本地方法栈是jvm操作系统方 ...

  6. stringbuilder调用tostring常量池_彻底弄懂java中的常量池

    作者:tracy_666链接:https://www.jianshu.com/p/55f65dac1b4b JVM常量池主要分为Class文件常量池.运行时常量池,全局字符串常量池,以及基本类型包装类 ...

  7. java 为什么需要常量池

    java中讲的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池. java内存模型中将内存分为堆和栈,其中堆为线程间共享的内存数据区域,栈 ...

  8. Java的字符串常量池

    字符串对象创建过程 先看一道面试题 // 这段代码创建了几个对象? String s = new String("卓卓"); 使用new关键字创建对象时,Java虚拟机会先在字符串 ...

  9. java字节码常量池_java字节码常量池处理说明

    1. 根据java的字节码格式说明,常量池中每一项的大小不一样的.运行时,若要通过数组索引获取具体的一项时, 必须要经过一定的处理才能根据数组下标进行处理,具体的实现原理实际上就是空间换时间,可以参考 ...

最新文章

  1. 利用gitHook实现自动部署
  2. 学习小米附加和大润发飞牛模式
  3. 有这一篇机器学习全够了
  4. 这10句话,迷茫时读一读。
  5. ABP前端使用阿里云angular2 UI框架NG-ZORRO分享
  6. (企业案例)使用Nacos持久化规则,改造sentinel-dashboard
  7. mysql 数据操作 单表查询 group by 注意
  8. 2017.10.9 DZY Loves Math VI 失败总结
  9. 因低俗色情网络文学作品 多个知名小说平台停更整改
  10. C++STL库中的map容器
  11. Management reporter 2012 与AX 2012
  12. django如何连接mysql_Django如何连接mysql
  13. 洛谷——P1144 最短路计数
  14. deb官方源、国内源
  15. matlab的罗马数字怎么写好看图解,【我想知道1—100的罗马数字怎样写啊就是ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪ这些等】作业帮...
  16. 异数OS TCP协议栈测试(五)--关于QOS与延迟
  17. 因特尔Edison第2站--mraa下gpio
  18. uva 12304(圆的相关函数模板)
  19. 详解ZStack Cloud v4.0:自研VPC网络模型实践指南
  20. iOS屏幕旋转及其基本适配方法

热门文章

  1. Leetcode周赛复盘——第 276 场力扣周赛
  2. [Java基础]Lambda表达式的省略模式
  3. Python中Numpy库中的Numpy常量
  4. 基本程序单元Activity—Activity生命周期之数据传递小程序
  5. 经典排序算法(7)——堆排序算法详解
  6. Caffe训练时出现了无数个Train net output #.....
  7. Codeforces Round #462 (Div. 2) C. A Twisty Movement dp + 思维转换
  8. 博弈论(基础概念+例题)
  9. 中心城镇问题(长链剖分优化树形dp)
  10. CodeForces 1517G Starry Night Camping(网络流最小割)