Class常量池

class常量池可以理解为是Class文件中的资源仓库。Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译期生成的各种字面量和符号引用。

一个Class文件的16进制大体结构如下图:

对应的含义如下,细节可以查oracle官方文档:

对于我们java程序员一般不会人工解析这种字节码文件,我们可以通过javap命令生成更可读的JVM字节码指令文件:javap -v Math.class,红色的框就是class常量池信息,常量池中主要存放两大类常量:字面量和符号引用。

字面量:

指由字母、数字等构成的字符串和数值常量,字面量只可以右值出现,

例如:int a=1 这里a为左值,1为右值,1就是字面量。

符号引用:

        符号引用是编译原理中的概念,是相对与直接引用来说,主要包括了以下三大类

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符   

例如:int a=1 ;a就是字段名称,就是一种符号引用。

运行时常量池

只有到运行时被加载到内存后,这些符号才有对应的内存地址,那么这些常量池一旦被装入内存就变成运行时常量池,对应的符号引用会转变为被加载到内存区域的代码的直接引用,也就是我们说的动态链接。

字符串常量池

字符串常量池的位置

  • jdk1.6及之前,有永久代,运行时常量池在永久代,运行时常量池包含字符串常量池。
  • jdk1.7:有永久代,但是已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里。
  • jdk1.8及以后:无永久代,运行时常量池在元空间,字符串常量池依然在堆里。

用一个程序证明字符串常量池在哪里:

运行结果:

  • jdk7及以上:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  • jdk6:Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

字符串常量池设计原理

字符串常量池底层是hotspot的c++实现的,类似一个HashTable,保存的本质上是字符串对象的引用。看下面的代码创建了多少个String对象?

1、在JDK1.6中,调用intern()首先会在字符串池中寻找equal()相等的字符串,例如字符串存在就返回该字符串在字符串池中的引用;假如字符串不存在,虚拟机会重新在永久代创建一个实例,将StringTable的一个表项指向这个新创建的实例:

2、在JDK1.7及以上版本,由于字符串不在永久代了,intern()做了一些修改,更方便的利用堆中的对象。字符串存在与jdk1.6一样,但是字符串不存在时不再需要重新创建实例,可以直接指向堆上的实例。

由上面两个图,可以理解为什么jdk1.6字符串池溢出会抛出OutOfMemoryError:PermGen space,而在jdk1.7及以上版本抛出OutOfMemoryError:Java heap space.

String常量池的几种例子解析:

实例1:

String s0="zhigan";

String s1="zhigan";

String s2="zhi" + "gan";

System.out.println( s0==s1 ); //true

System.out.println( s0==s2 );true

分析:因为例子中的s0和s1中的“zhigan”都是字符串常量,他们在编译期就被确定了,所以s0=s1为true;而“zhi”和“gan”也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2同样在编译期被优化为一个字符串常量“zhigan”,所以s2也是常量池中的“zhigan”的一个引用,所以我们得出s0==s1==s2.

实例2:
String s0="zhigan";
String s1=new String("zhigan");
String s2=“zhi”+new String("gan");
System.out.println( s0==s1 ); // false
System.out.println( s0==s2 ); // false
System.out.println( s1==s2 ); // false
分析:用new String()创建的字符串不是常量,不能在编译期就确定,所用用new String()创建的字符串不放入常量池中,它们有自己的地址空间。s0还是常量池中"zhigna"的引用,s1因为无法在编译期确定,所以是运行时创建的新对象"zhigan"的引用,s2因为有后半部分new String("gan")所以也无法在编译期确定,所以也是一个新创建对象"zhigan"的引用。
实例3:
String a="a1";
String b="a"+1;
System.out.println( a==b); // true
String a="atrue";
String b="a"+"true";
System.out.println( a==b); // true
String a="a3.4";
String b="a"+3.4;
System.out.println( a==b); // true
分析:JVM对于字符串常量的"+"号连接,将在程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,故上面的程序最后的结果都为true.
实例4:
String a="ab";
String bb="b";      
String b="a"+bb;
System.out.println( a==b); // false
分析:JVM对于字符串引用,由于字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a"+bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b,所以上面的结果为false.(如果bb是一个方法的返回结果,同样的原因)
实例5:

String a="ab";
final String bb="b";      
String b="a"+bb;
System.out.println( a==b); // true

分析:和例4中唯一不同的是bb字符串被final修饰,对于final修饰的变量,它在编译期被解析为常量值的一个本地拷贝存储到自己的常量池或嵌入到他的字节码流中,所以此时的"a"+bb和"a"+"b"效果是一样的,故结果为true.
实例6:
String s = "a" + "b" + "c"; //就等价于String s = "abc";
String a = "a";
String b = "b";
String c = "c";
String s1 = a + b + c;
分析:s1这个就不一样,可以通过观察器JVM指令码发现s1的"+"操作会变成如下:
StringBuilder temp=new StringBuilder();
temp.append(a).append(b).append(c);
String s=temp.toString();

八种基本类型的包装类和对象池

        java中基本类型的包装类的大部分都实现了常量池技术,严格来说应该叫对象池,再堆上,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外 Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。因为一般这种比较小的数用到的概率相对较大。

例子截图如下:

补充:Integer a=12;底层实际上是执行了Integer.valueOf(12),里面用到了IntegerCache对象池

深度剖析Java常量池相关推荐

  1. (全网最详细最有深度)超过1W字深度剖析JVM常量池

    字符串常量池存储在堆内存空间中,创建形式如下图所示. 当使用String a="Hello"这种方式创建字符串对象时,JVM首先会先检查该字符串对象是否存在与字符串常量池中,如果存 ...

  2. JAVA8常量池监控_深入探索Java常量池

    Java的常量池通常分为两种:静态常量池和运行时常量池 静态常量池:class文件中的常量池,class文件中的常量池包括了字符串(数字)字面值,类和方法的信息,占用了class文件的大部分空间. 运 ...

  3. Java常量池的大概理解

    触摸java常量池 java常量池是一个经久不衰的话题,也是面试官的最爱,题目花样百出,小菜早就对常量池有所耳闻,这次好好总结一下. 理论 小菜先拙劣的表达一下jvm虚拟内存分布: 程序计数器是jvm ...

  4. 深度剖析Java数据结构之迭代器(Iterator)

    一.什么是迭代器 我们知道,JVM是用C/C++编写的.在百度百科中,迭代器是解释是迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定 ...

  5. Java常量池[乐乐独记]

    Java常量池[乐乐独记] 1.字面量和符号引用 1.1.字面量 1.2.符号引用 2.常量池 2.1.静态常量池 2.2.运行时常量池 2.3.字符串常量池 2.3.1.字符串常量池的概念 2.3. ...

  6. Java常量池解析与字符串intern简介

    在Java应用程序运行时,Java虚拟机会保存一份内部的运行时常量池,它区别于class文件的常量池,是class文件常量池映射到虚拟机中的数据结构. 关于class文件常量池的部分可以参考之前的博文 ...

  7. JVM - 深入剖析字符串常量池

    文章目录 字符串常量池 位置的变更 JVM对字符串常量池的优化 字符串的常见创建方式 (1.7+) 直接赋值字符串 new String() intern 经典面试题 下列代码创建几个对象 案例 案例 ...

  8. java 常量池详解

    参考资料:http://chenzehe.iteye.com/blog/1727062 注意: 1.首先,我们平时在讨论字符串新建问题时所说的常量池其实指的是字符串常量池.并不是运行时常量池,更加不是 ...

  9. Java常量池理解与总结

    2019独角兽企业重金招聘Python工程师标准>>> 一.相关概念 什么是常量 用final修饰的成员变量表示常量,值一旦给定就无法改变! final修饰的变量有三种:静态变量.实 ...

最新文章

  1. C#调用TSC条码打印机打印二维码(转)
  2. 通过安装scl软件集,使用高版本gcc的方法
  3. 盘点VS2015 预览版的5个新特性
  4. Stark 组件:快速开发神器 —— 页面显示
  5. SAP S/4HANA里KPI tile的一个具体例子
  6. Java 9 –终极功能列表
  7. [密码学基础][每个信息安全博士生应该知道的52件事]52.先进的应用概念 系统的大致安全需求
  8. 网站实现点击 “加入收藏 ”功能 - 代码篇
  9. python小工具(一)
  10. python 读取、写入 pkl文件
  11. python 重启电脑_如何在系统重启后恢复Python脚本?
  12. 学习笔记--maven
  13. winform自定义消息提示框定时自动关闭
  14. python机器学习-乳腺癌细胞挖掘(基于真实美国临床数据)
  15. matlab上的派克变换变换,Matlab_Simulink中Clark变换和Park变换的深度总结
  16. 协同软件市场一盘散沙 春种能否秋收心中没底
  17. linux pv修改大小,Linux下扩容系统容量和删除unknown PV
  18. 【Ansible】 Ansible 模块 setup 与 stat 模块用法
  19. ObiFruid创建流体,ObiFruid学习笔记
  20. docker 应用系列(一)--- 一步步搭建虚拟机 docker 环境 附有 vue-cli + nginx 应用...

热门文章

  1. 阿里规范-为什么不允许魔法值(即未经定义的常量)出现在代码中?
  2. msfconsole之制作windows木马并成功获取shell
  3. 计算机excl知识题,计算机excel考试试题「附答案」
  4. P4554 小明的游戏(双端bfs)
  5. Thinkpad-L14双系统装机
  6. 进制的转换,ipv4
  7. Idea配置翻译插件
  8. 900页文档比对只需5分钟?鸿翼InWise文档比对,以人工智能撬动办公效率杠杆
  9. 正则表达式-连续多位相同字符判断的正则表达式
  10. ChatGPT AI绘图Siri快捷指令,根据描述生成图片