目录

常量池

静态常量池

运行时常量池

字符串常量池

三个常量池的关系

其随jdk版本的变化

常量池

请注意常量池是线程共享数据区,常量池的内容:

常量池的好处:

常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。

(1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。

(2)节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

静态常量池

静态常量池也就是Class文件中的常量池,下面用一张图来看看静态常量池在Class文件中的位置:

从上图可以看出,Class文件中包括:

魔数:它的唯一作用是确定这个文件是否可以被JVM接受。很多文件储存标准中都使用魔数来进行身份识别的,其占用这个文件的前四个字节。

版本号:第5和第6个字节是副版本号,第7个和第8 个是主版本号。

常量池计数器:也就是常量池的入口,代表常量池的容量计数器。

常量池:常量池中主要存放两类常量:字面量和符号引用。字面量比较接近Java语言层面的常量概念。就是我们什么提到的常量。而符号引用则属于编译原理的方面的概念。包括以下三类常量:

(1)类和接口的全限定名

(2)字段的名称和描述符

(3)方法的名称和描述符

静态常量池用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。其中符号引用其实引用的就是常量池里面的字符串,但符号引用不是直接存储字符串,而是存储字符串在常量池里的索引。

当Class文件被加载完成后,java虚拟机会将静态常量池里的内容转移到运行时常量池里,在静态常量池的符号引用有一部分是会被转变为直接引用的,比如说类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。

运行时常量池

运行时常量池(Runtime Constant Pool)是方法区的一部分。对于运行时常量池,Java虚拟机规范没有做任何细节的要求,不同的提供商实现的虚拟机可以按照自己的需要来实现这个内存区域。不过,一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。

运行时常量池还有个更重要的的特征:动态性。Java要求,编译期的常量池的内容可以进入运行时常量池,运行时产生的常量也可以放入池中。常用的是String类的intern()方法。

既然运行时常量池是方法区的一部分自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

字符串常量池(string pool)

字符串常量池存在运行时常量池之中(在JDK7之前存在运行时常量池之中,在JDK7已经将其转移到堆中)。

字符串常量池的存在使JVM提高了性能和减少了内存开销。

使用字符串常量池,每当我们使用字面量(String s=”1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s(引用s在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)。

使用字符串常量池,每当我们使用关键字new(String s=new String(”1”);)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中创建该对象的副本,然后将堆中对象的地址赋值给引用s,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中创建该对象的副本,然后将堆中对象的地址赋值给引用s。

下图是API说明:

翻译为:“初始化一个新创建的字符串对象,以便它表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。除非需要显式的原始副本,否则使用此构造函数是不必要的,因为字符串是不可变的。”

由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串。

鉴于String.intern()在API上的说明和new String(“a”)创建字符串(创建了两个对象,如果字符串常量池存在则是一个对象)在官方API上的说明,我个人认为字符串常量池存的是字符串对象,当然在JKD7之后,常量池中存储的可能是堆对象的引用,我的另一篇关于String类的文章会讲到。(可用javap -c反编译即可得到JVM执行的字节码内容,javap -verbose 反编译查看常量池内容)

在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是key-value键值对,上面也说了,常量池是线程共享数据区,这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享(享元模式)

三个常量池的关系

通过上面的描述,我们可以总结出三个常量池的关系如下:

JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。

静态常量池用于存放编译期生成的各种字面量和符号引用,而当类加载到内存中后,jvm就会将静态常量池中的内容存放到运行时常量池中。而字符串常量池存的是引用值,其存在于运行时常量池之中。

其随jdk版本的变化

在JDK6及之前的版本:

静态常量池在Class文件中。

运行时常量池在Perm Gen区(也就是方法区)中。(所谓的方法区是在Java堆的一个逻辑部分,为了与Java堆区别开来,也称其为非堆(Non-Heap),那么Perm Gen(永久代)区也被视为方法区的一种实现。)

字符串常量池在运行时常量池中。

在JDK7版本:

静态常量池在Class文件中。

运行时常量池依然在Perm Gen区(也就是方法区)中。在JDK7版本中,永久代的转移工作就已经开始了,将譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。但是运行时常量池依然还存在,只是很多内容被转移,其只存着这些被转移的引用。网上流传的一些测试运行时常量池转移的方式或者代码,其实是对字符串常量池转移的测试。

字符串常量池被分配到了Java堆的主要部分(known as the young and old generations)。也就是字符串常量池从运行时常量池分离出来了,下面是官方给出的改动摘要:

上文翻译:

在JDK 7中,实例化的字符串不再分配在Java堆的永久生成中,而是分配在Java堆的主要部分(称为年轻和老一代),以及另一个应用程序创建的对象。此更改将导致更多数据驻留在主Java堆中,并且永久生成中的数据更少,因此可能需要调整堆大小。由于此更改,大多数应用程序将只看到堆使用中相对较小的差异,但是加载许多类或大量使用该String.intern()方法的较大应用程序将看到更显着的差异。

在JDK8版本:

静态常量池在Class文件中。

JVM已经将运行时常量池从方法区中移了出来,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。同时永久代被移除,以元空间代替。元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。其主要用于存放一些元数据。

字符串常量池存在于Java堆中。

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

  1. 在java程序中定义的类有两种成员_java试题 急需答案 谢谢!!!

    三.填空(每小题2分,共10分)1.在Applet中,创建一个具有10行45列的多行文本区对象ta的语句为:2.创建一个标识有"关闭"字样的标签对象gb的语句为.3.方法是一种仅有 ...

  2. Java中线程的创建有两种方式

    Java中继承thread类与实现Runnable接口的区别 Java中线程的创建有两种方式: 1.  通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2.  通过 ...

  3. java random构造方法_Java中的Random()函数及两种构造方法

    Java中存在着两种Random函数: java.lang.Math.Random; 调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范围是 ...

  4. Java中的string定义的两种方法和区别

    java中的String定义的两种方法和区别 第一种:new方式 String s1 = new String("hello world"); String s2 = new St ...

  5. java中map类型_Java中Map类型遍历的两种方式对比

    Java中Map类型是存储键值对数据的类型,在编程过程经常使用,进行遍历操作对于每个Java程序员都不会模式,下面总结两种常用的遍历方式(一种keySet,一种entrySet),通过对比让你明白使用 ...

  6. java定义一个长方形类,该类中具有长方形长宽两种属性,并具有相应的构造方法属性访问方法,计算长方形的周长和面积的方法,要求输出长是5,宽是4的长方形

    定义一个长方形类,该类中具有长方形长宽两种属性,并具有相应的构造方法 属性访问方法,计算长方形的周长和面积的方法,要求输出长是5,宽是4的长方形 public class test3 {public ...

  7. JSP页面中的pageEncoding和contentType两种属性

    关于JSP页面中的pageEncoding和contentType两种属性的区别: pageEncoding是jsp文件本身的编码 contentType的charset是指服务器发送给客户端时的内容 ...

  8. Android AsyncTask两种线程池分析和总结

    转自:http://bbs.51cto.com/thread-1114378-1-1.html Android AsyncTask两种线程池分析和总结 (一)    前言 在android Async ...

  9. Android之AsyncTask两种线程池分析和总结

    Android AsyncTask两种线程池分析和总结 (一)    前言 在android AsyncTask里面有两种线程池供我们调用 1.    THREAD_POOL_EXECUTOR, 异步 ...

最新文章

  1. Python分析离散心率信号(下)
  2. 一个强迫症的Git 选择
  3. 事件标志组的删除与状态查询
  4. 再谈java乱码:GBK和UTF-8互转尾部乱码问题分析
  5. 启动和停止Oracle服务bat脚本
  6. ITK:从体积生成切片
  7. scrapy 解决Redirecting 301 302重定向问题
  8. 计算机网络技术专业的规划,计算机网络技术专业建设规划
  9. 机器人 魂斗罗铁血兵团_《魂斗罗铁血兵团:反叛》XBLA游戏下载
  10. 【mysql】显式加锁
  11. 百度视觉技术部实习生招聘
  12. 中兴通讯携手ACCYOURATE发布“YouCare”5G智慧T恤
  13. topic1:Qt入门之搭建环境与hello world看Qt开发框架
  14. 49. PHP 页面静态化(2)
  15. Java贪吃蛇游戏开发
  16. MT4 中各种指标线的解释
  17. 电子统计台账:中文标记月度流水账格式数据的转换,以及过滤模板的普遍适配性
  18. 安装广告拦截插件abp
  19. 基于WordNet的英文同义词、近义词相似度评估及代码实现
  20. Android开发入门教程pdf

热门文章

  1. 27英寸苹果新款iMac上架:升级十代酷睿+Radeon 5000系显卡
  2. www.baidu.com www.topka.cn www.xiaoxiaose.com
  3. css!important_如何解决CSS特殊性问题以及何时使用!important关键字
  4. 我的秋招总结(百度,搜狗,京东,美团,科大讯飞,新华三,国家互联网应急中心,微盟,ofo面经)
  5. 终于有人把推荐系统讲明白了
  6. 联想 E49 Win8 的wifi 不可用
  7. 游戏数字华容道的改进
  8. javascript判断是否为整型
  9. Mahout使用K-Means进行中文文本聚类
  10. 科普|一体化污水处理设备生物膜的形成及挂膜调试