常量池(constant_pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量和符号引用。运行时常量池是方法区的一部分。

在Class文件结构中,最头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

类和接口的全限定名

字段名称和描述符

方法名称和描述符

Java中八种基本类型的包装类的大部分都实现了常量池技术,它们是Byte、Short、Integer、Long、Character、Boolean,另外两种浮点数类型的包装类(Float、Double)则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值在-128到127时才可使用对象池。

看下面代码:

Java代码  
  1. /**
  2. * Huisou.com Inc.
  3. * Copyright (c) 2011-2012 All Rights Reserved.
  4. */
  5. /**
  6. * @description
  7. * @package
  8. * @title Test.java
  9. * @author chenzehe
  10. * @email
  11. * @version
  12. * @updateUser
  13. * @create 2011-12-21 上午11:27:48
  14. * @update 2011-12-21 上午11:27:48
  15. */
  16. public class Test {
  17. public static void main(String[] args) throws Exception {
  18. Integer a = 127;
  19. Integer b = 127;
  20. Integer c = 128;
  21. Integer d = 128;
  22. System.out.println(a == b);// 输出true
  23. System.out.println(c == d);// 输出false
  24. }
  25. }

使用javap查看生成的字节码:

Java代码  
  1. E:\chenzehe\workspace_b2b_3th\test\src>javap -verbose Test
  2. Compiled from "Test.java"
  3. public class Test extends java.lang.Object
  4. SourceFile: "Test.java"
  5. minor version: 0
  6. major version: 50
  7. Constant pool:
  8. const #1 = Method       #6.#21; //  java/lang/Object."<init>":()V
  9. const #2 = Method       #22.#23;        //  java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  10. const #3 = Field        #24.#25;        //  java/lang/System.out:Ljava/io/PrintStream;
  11. const #4 = Method       #26.#27;        //  java/io/PrintStream.println:(Z)V
  12. const #5 = class        #28;    //  Test
  13. const #6 = class        #29;    //  java/lang/Object
  14. const #7 = Asciz        <init>;
  15. const #8 = Asciz        ()V;
  16. const #9 = Asciz        Code;
  17. const #10 = Asciz       LineNumberTable;
  18. const #11 = Asciz       main;
  19. const #12 = Asciz       ([Ljava/lang/String;)V;
  20. const #13 = Asciz       StackMapTable;
  21. const #14 = class       #30;    //  "[Ljava/lang/String;"
  22. const #15 = class       #31;    //  java/lang/Integer
  23. const #16 = class       #32;    //  java/io/PrintStream
  24. const #17 = Asciz       Exceptions;
  25. const #18 = class       #33;    //  java/lang/Exception
  26. const #19 = Asciz       SourceFile;
  27. const #20 = Asciz       Test.java;
  28. const #21 = NameAndType #7:#8;//  "<init>":()V
  29. const #22 = class       #31;    //  java/lang/Integer
  30. const #23 = NameAndType #34:#35;//  valueOf:(I)Ljava/lang/Integer;
  31. const #24 = class       #36;    //  java/lang/System
  32. const #25 = NameAndType #37:#38;//  out:Ljava/io/PrintStream;
  33. const #26 = class       #32;    //  java/io/PrintStream
  34. const #27 = NameAndType #39:#40;//  println:(Z)V
  35. const #28 = Asciz       Test;
  36. const #29 = Asciz       java/lang/Object;
  37. const #30 = Asciz       [Ljava/lang/String;;
  38. const #31 = Asciz       java/lang/Integer;
  39. const #32 = Asciz       java/io/PrintStream;
  40. const #33 = Asciz       java/lang/Exception;
  41. const #34 = Asciz       valueOf;
  42. const #35 = Asciz       (I)Ljava/lang/Integer;;
  43. const #36 = Asciz       java/lang/System;
  44. const #37 = Asciz       out;
  45. const #38 = Asciz       Ljava/io/PrintStream;;
  46. const #39 = Asciz       println;
  47. const #40 = Asciz       (Z)V;
  48. {
  49. public Test();
  50. Code:
  51. Stack=1, Locals=1, Args_size=1
  52. 0:   aload_0
  53. 1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
  54. 4:   return
  55. LineNumberTable:
  56. line 18: 0
  57. public static void main(java.lang.String[])   throws java.lang.Exception;
  58. Code:
  59. Stack=3, Locals=5, Args_size=1
  60. 0:   bipush  127
  61. 2:   invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  62. 5:   astore_1
  63. 6:   bipush  127
  64. 8:   invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  65. 11:  astore_2
  66. 12:  sipush  128
  67. 15:  invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  68. 18:  astore_3
  69. 19:  sipush  128
  70. 22:  invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
  71. 25:  astore  4
  72. 27:  getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
  73. 30:  aload_1
  74. 31:  aload_2
  75. 32:  if_acmpne       39
  76. 35:  iconst_1
  77. 36:  goto    40
  78. 39:  iconst_0
  79. 40:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
  80. 43:  getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
  81. 46:  aload_3
  82. 47:  aload   4
  83. 49:  if_acmpne       56
  84. 52:  iconst_1
  85. 53:  goto    57
  86. 56:  iconst_0
  87. 57:  invokevirtual   #4; //Method java/io/PrintStream.println:(Z)V
  88. 60:  return
  89. LineNumberTable:
  90. line 20: 0
  91. line 21: 6
  92. line 22: 12
  93. line 23: 19
  94. line 24: 27
  95. line 25: 43
  96. line 26: 60
  97. StackMapTable: number_of_entries = 4
  98. frame_type = 255 /* full_frame */
  99. offset_delta = 39
  100. locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class java/lang/Inte
  101. ger, class java/lang/Integer ]
  102. stack = [ class java/io/PrintStream ]
  103. frame_type = 255 /* full_frame */
  104. offset_delta = 0
  105. locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class java/lang/Inte
  106. ger, class java/lang/Integer ]
  107. stack = [ class java/io/PrintStream, int ]
  108. frame_type = 79 /* same_locals_1_stack_item */
  109. stack = [ class java/io/PrintStream ]
  110. frame_type = 255 /* full_frame */
  111. offset_delta = 0
  112. locals = [ class "[Ljava/lang/String;", class java/lang/Integer, class java/lang/Integer, class java/lang/Inte
  113. ger, class java/lang/Integer ]
  114. stack = [ class java/io/PrintStream, int ]
  115. Exceptions:
  116. throws java.lang.Exception
  117. }

Integer a = 127;对应的指令为:

Java代码  
  1. 0:   bipush  127
  2. 2:   invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

Integer c = 128;对应的指令为:

Java代码  
  1. 12:  sipush  128
  2. 15:  invokestatic    #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

bipush指令意思是将单字节的常量值(-128~127)推送到栈顶

sipush指令意思是将短型的常量值(-32768~32767)推送到栈顶

invokestatic指令意思是调用静态方法,这里调用的是常量池中#2指向的方法java/lang/Integer.valueOf,查看Integer.valueOf方法:

Java代码  
  1. public static Integer valueOf(int i) {
  2. final int offset = 128;
  3. if (i >= -128 && i <= 127) { // must cache
  4. return IntegerCache.cache[i + offset];
  5. }
  6. return new Integer(i);
  7. }

IntegerCache代码:

Java代码  
  1. private static class IntegerCache {
  2. private IntegerCache(){}
  3. static final Integer cache[] = new Integer[-(-128) + 127 + 1];
  4. static {
  5. for(int i = 0; i < cache.length; i++)
  6. cache[i] = new Integer(i - 128);
  7. }
  8. }

其它封装类如下:

Java代码  
  1. //Boolean类也实现了常量池技术
  2. Boolean bool1=true;
  3. Boolean bool2=true;
  4. System.out.println(bool1==bool2); //输出true
  5. //浮点类型的包装类没有实现常量池技术
  6. Double d1=1.0;
  7. Double d2=1.0;
  8. System.out.println(d1==d2); //输出false

String s =  new  String( "xyz" );  在运行时涉及 几个String实例?

两个,一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例。

String中的final用法和理解:

final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象的变化,final是不负责的。

Java代码  
  1. final StringBuffer a = new StringBuffer("111");
  2. final StringBuffer b = new StringBuffer("222");
  3. a=b;//此句编译不通过
  4. final StringBuffer a = new StringBuffer("111");
  5. a.append("222");//编译通过
Java代码  
  1. String a = "a1";
  2. String b = "a" + 1;
  3. System.out.println((a == b)); //result = true
  4. String a = "atrue";
  5. String b = "a" + "true";
  6. System.out.println((a == b)); //result = true
  7. String a = "a3.4";
  8. String b = "a" + 3.4;
  9. System.out.println((a == b)); //result = true

JVM对于字符串常量的"+"号连接,将程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值,拿"a" + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。

Java代码  
  1. String a = "ab";
  2. String bb = "b";
  3. String b = "a" + bb;
  4. System.out.println((a == b)); //result = false

JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。

Java代码  
  1. String a = "ab";
  2. final String bb = "b";
  3. String b = "a" + bb;
  4. System.out.println((a == b)); //result = true

和上面唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。

Java代码  
  1. String a = "ab";
  2. final String bb = getBB();
  3. String b = "a" + bb;
  4. System.out.println((a == b)); //result = false
  5. private static String getBB() {
  6. return "b";
  7. }

JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,故上面程序的结果为false。

通过上面4个例子可以得出得知:

Java代码  
  1. String  s  =  "a" + "b" + "c";

就等价于String s = "abc";  int i = 1+2+3;也等价于int i = 6;

Java代码  
  1. String  a  =  "a";
  2. String  b  =  "b";
  3. String  c  =  "c";
  4. String  s  =   a  +  b  +  c;

这个就不一样了,最终结果等于:

Java代码  
  1. StringBuffer temp = new StringBuffer();
  2. temp.append(a).append(b).append(c);
  3. String s = temp.toString();

由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:

Java代码  
  1. public class Test {
  2. public static void main(String args[]) {
  3. String s = null;
  4. for(int i = 0; i < 100; i++) {
  5. s += "a";
  6. }
  7. }
  8. }

每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。 如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。

String.intern()解析

Java语言并不要求常量一定只能在编译期产生,运行时也可能将新的常量放入常量池中,这种特性用的最多的就是String.intern()方法。

String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加 一个Unicode等于str的字符串并返回它的引用。

Java代码  
  1. String s0= "xyz";
  2. String s1=new String("xyz");
  3. String s2=new String("xyz");
  4. System.out.println(s0==s1);
  5. s1.intern();
  6. s2=s2.intern(); //把常量池中“pku”的引用赋给s2
  7. System.out.println( s0==s1);
  8. System.out.println( s0==s1.intern() );
  9. System.out.println( s0==s2 );

输出为: 
false 
false //虽然执行了s1.intern(),但它的返回值没有赋给s1 
true //说明s1.intern()返回的是常量池中”pku”的引用 
true

有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局String表中,如果具有相同值的Unicode字符串 已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说的这个全局的String 表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的:

Java代码  
  1. String s1=new String("xyz");
  2. String s2=s1.intern();
  3. System.out.println( s1==s1.intern() );
  4. System.out.println( s1+" "+s2 );
  5. System.out.println( s2==s1.intern() );

输出为: 
false 
xyz xyz 
true

JVM常量池和八种基本数据及字符串相关推荐

  1. 《性能调优之JVM》❤️04JVM常量池详解之深入理解Class常量池、运行时常量池、字符串常量池、八种基本数据类型的包装类和对象池

    JVM常量池详解 一.Class常量池 1.1字面量 1.2符号引用 二.运行时常量池 三.字符串常量池 3.1设计思想 3.2设计原理 3.3字符串常量池位置 3.4三种字符串操作 四.八种基本数据 ...

  2. JVM出现OOM的八种原因及解决办法

    本文源自转载:JVM出现OOM的八种原因及解决办法 目录 一.堆溢出 1.1 原因 1.2解决方法 二.永久代/元空间溢出 2.1 原因 2.2 解决方法 三.GC overhead limit ex ...

  3. JVM常量池最全详解-常量池/运行时常量池/字符串常量池/基本类型常量池,看这一篇就够了

    JVM常量池最全详解-常量池/运行时常量池/字符串常量池/基本类型常量池,看这一篇就够了! 常量池详解 1. 字面量和符号引用 1.1 字面量 1.2 符号引用 2. 常量池vs运行时常量池 3. 常 ...

  4. java 更改 常量池_JVM中三个常量池(两种常量池)的解析及其随jdk版本的变化

    目录 常量池 静态常量池 运行时常量池 字符串常量池 三个常量池的关系 其随jdk版本的变化 常量池 请注意常量池是线程共享数据区,常量池的内容: 常量池的好处: 常量池是为了避免频繁的创建和销毁对象 ...

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

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

  6. 详解JVM常量池、Class常量池、运行时常量池、字符串常量池(心血总结)

    写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...

  7. 八种基本数据类型变量声明

    package com.hisoft.java.Bean;public class Test1 { static int age=12; // String str = new String(&quo ...

  8. JVM常量池和运行时常量池

    一.类的二进制字节码包含哪些信息 要理解常量池是什么,先看看类的二进制字节码包含哪些信息!!! 常量池 类的基本信息(比如:类的访问权限.类的名称.实现了哪些接口) 类的方法定义(包含了虚拟机指令,也 ...

  9. Java 线程池 ThreadPoolExecutor 八种拒绝策略浅析

    前言 谈到 Java 的线程池最熟悉的莫过于 ExecutorService 接口了,jdk1.5 新增的 java.util.concurrent 包下的这个 api,大大的简化了多线程代码的开发. ...

  10. Java中几种常量池(字符串常量池, Class常量池, 运行时常量池)的区别与联系

    简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去CSDN.博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将我自认为对的理解写下来与 ...

最新文章

  1. 考研数学一2015年真题整理
  2. BZOJ2054: 疯狂的馒头(并查集)
  3. linux mysql 文件恢复_linux下误删数据文件恢复
  4. 技术者利用wordpress+阿里云服务器+LAMP新搭建的博客网站:www.youngxy.top
  5. php全选删除,php+ajax简单实现全选删除的方法
  6. 自定义检验注解_多注解自定义参数校验
  7. HDU 6124 Euler theorem
  8. 英特尔与Verizon合力推动5G技术 新网络传输革命即将来临
  9. mysql 函数无法访问_mysql 中出现:不能打开到主机的连接,在端口3306: 连接失败...
  10. ios应用提交审核出现的问题总结
  11. Java 基础 —— Character 与 String
  12. CCIE学习(40)—— OSPF设计与LSA类型(三)
  13. Nginx部署前端项目
  14. VBA字典数组转置维度变化
  15. html使用视频给网页做背景
  16. kodi 下载插件失败/无法刮削
  17. 2019-11 前端技术汇总
  18. Python中的pillow(PIL)
  19. Yapi使用(快速入门)
  20. 超级计算机用什么芯片,神威是第一台完全使用中文芯片的超级计算机吗?

热门文章

  1. 网站日志统计案例分析与实现
  2. 基于mAppWidget实现手绘地图--索引DEMO
  3. 拓端tecdat|R语言弹性网络Elastic Net正则化惩罚回归模型交叉验证可视化
  4. 拓端tecdat|R语言隐马尔可夫模型HMM识别不断变化的市场条件
  5. android pokemon go,安卓Pokemon GO懒人版
  6. Ubuntu18 yolov5使用ncnn部署
  7. 【记录】word 插入高亮代码
  8. Caffe学习:Data
  9. 鸿蒙2.0内测版手机版,不再遮遮掩掩,鸿蒙2.0测试版下月发布,用于手机内测
  10. 24小时计时器设计logisim_c++日期、时间和计时器