精华内容
下载资源
问答
  • Java中的常量池

    2021-03-07 23:12:04
    Java中的常量池主要有字符串常量值,class常量池,运行时常量池 二:字符串常量池 在JVM中,为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开辟一块内存,用于保存字符串常量,这个内存区域被...

    一:在Java中有那些常量池?

    谈到常量池,在Java体系中,共用四种常量池。分别是字符串常量池Class常量池运行时常量池,JAVA 基本类型的封装类及对应常量池

    二:字符串常量池

    在JVM中,为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池。

    当代码中出现双引号形式(字面量)创建字符串对象时,JVM 会先对这个字符串进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回;否则,创建新的字符串对象,然后将   这个引用放入字符串常量池,并返回该引用。

    这种机制,就是字符串驻留或池化。

    字符串常量池的位置

    在JDK 7以前的版本中,字符串常量池是放在永久代中的。

    因为按照计划,JDK会在后续的版本中通过元空间来代替永久代,所以首先在JDK 7中,将字符串常量池先从永久代中移出,暂时放到了堆内存中。

    在JDK 8中,彻底移除了永久代,使用元空间替代了永久代,于是字符串常量池再次从堆内存移动到永久代中

    三:class常量池

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

    由于不同的Class文件中包含的常量的个数是不固定的,所以在Class文件的常量池入口处会设置两个字节的常量池容量计数器,记录了常量池中常量的个数。

    class常量池中有什么?

     常量池中主要存放两大类常量:字面量(literal)和符号引用(symbolic references)。

    字面量:

    在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串;而有很多也对布尔类型和字符类型的值也支持字面量表示;还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法。

     以上是关于计算机科学中关于字面量的解释,并不是很容易理解。说简单点,字面量就是指由字母、数字等构成的字符串或者数值。

    字面量只可以右值出现,所谓右值是指等号右边的值,如:int a=123这里的a为左值,123为右值。在这个例子中123就是字面量。

    int a = 123;
    String s = "hollis";
    

     符号引用:

    常量池中,除了字面量以外,还有符号引用,那么到底什么是符号引用呢。

    符号引用是编译原理中的概念,是相对于直接引用来说的。主要包括了以下三类常量: * 类和接口的全限定名 * 字段的名称和描述符 * 方法的名称和描述符

    这也就可以印证前面的常量池中还包含一些com/hollis/HelloWorldmain([Ljava/lang/String;)V等常量的原因了。

    四:运行时常量池

    运行时常量池是方法区的一部分,所以也是全局贡献的,我们知道,jvm在执行某个类的时候,必须经过加载、链接(验证、准备、解析)、初始化,在第一步加载的时候需要完成:

    • 通过一个类的全限定名来获取此类的二进制字节流
    • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
    • 在内存中生成一个类对象,代表加载的这个类,这个对象是java.lang.Class,它作为方法区这个类的各种数据访问的入口。

    类对象和普通对象是不同的,类对象是在类加载的时候完成的,是jvm创建的并且是单例的,作为这个类和外界交互的入口, 而普通的对象一般是在调用new之后创建。

    上面的第二条,将class字节流代表的静态存储结构转化为方法区的运行时数据结构,其中就包含了class文件常量池进入运行时常量池的过程,这里需要强调一下不同的类共用一个运行时常量池,同时在进入运行时常量池的过程中,多个class文件中常量池相同的字符串,多个class文件中常量池中相同的字符串只会存在一份在运行时常量池,这也是一种优化。

    运行时常量池的作用是存储java class文件常量池中的符号信息,运行时常量池中保存着一些class文件中描述的符号引用,同时在类的解析阶段还会将这些符号引用翻译出直接引用(直接指向实例对象的指针,内存地址),翻译出来的直接引用也是存储在运行时常量池中。

    运行时常量池相对于class常量池一大特征就是具有动态性,java规范并不要求常量只能在运行时才产生,也就是说运行时常量池的内容并不全部来自class常量池,在运行时可以通过代码生成常量并将其放入运行时常量池中,这种特性被用的最多的就是String.intern()。

    五:JAVA 基本类型的封装类及对应常量池

    java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外上面这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象。

     

    展开全文
  • java中的常量池

    2021-04-05 20:57:24
    常量池是java开发过程中经常使用到的,但java中有几种类型的常量池,而且这些常量池分别是用来做什么工作的,对jvm的性能有什么影响,这些就是笔者接下来要记录的。 目录一、什么是常量池?它是用来干什么的?二、...

    常量池是java开发过程中经常使用到的,但java中有几种类型的常量池,而且这些常量池分别是用来做什么工作的,对jvm的性能有什么影响,这些就是笔者接下来要记录的。


    一、什么是常量池?它是用来干什么的?

    一般来说我们所说的常量池都是运行时常量池,它是用来存储字符串(数字)字面量、类、方法等信息的,存储位置是在方法区。

    二、java中有几种类型的常量池?

    java中的常量池主要分为两种:

    • 1、class常量池:可以理解为是Class文件中的资源仓库。用于存放编译期生成的各种字面量(Literal)和符号引用(Symbolic References)。
    • 2、运行时常量池:class常量池现中存储的是静态信息,只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装 入内存就变成运行时常量池。

    三、字符串的两种创建方式有什么区别?

    String str = muyichen; //str指向常量池中的引用

    • 这种方式创建的字符串对象,只会在常量池中。 因为有"muyichen"这个字面量,创建对象str的时候,JVM会先去常量池中通过 equals(key) 方法,判断是否有相同的对象 如果有,则直接返回该对象在常量池中的引用; 如果没有,则会在常量池中创建一个新对象,再返回引用。

    String str = new String(“muyichen”); //str指向内存中的对象的引用

    • 这种方式会保证字符串常量池和堆中都有这个对象,没有就创建,最后返回堆内存中的对象引用。 步骤大致如下: 因为有"muyichen"这个字面量,所以会先检查字符串常量池中是否存在字符串"muyichen" ,如果不存在,先在字符串常量池里创建一个字符串对象,再去内存中创建一个字符串对象"muyichen"; 如果存在的话,就直接去堆内存中创建一个字符串对象"muyichen"; 最后,将内存中的引用返回。

    四、字符串的intern()方法

    字符串的intern()方法,按jdk的版本来分的话是有两种的:

    • 1、jdk1.6之前,由于当时的字符串常量池是放在运行时常量池里面,而运行时常量池是放在永久代(元空间/方法区)里的,所以jdk1.6之前的intern()方法是:如果字符串常量池已经包含一个等于此String对象的字符串 (用equals(oject)方法确定),则返回池中的字符串;否则要将该字符串复制到字符串常量池中再返回池中的字符串。
    • 2、jdk1.7之后,此时的字符串常量池从运行时常量池中分离了出来,放到了堆内存中去了,所以此时的intern()方法是:如果池已经包含一个等于此String对象的字符串 (用equals(oject)方法确定),则返回池中的字符串。否则,将intern返回的引用指向当前字符串。

    五、常量池对jvm的性能有哪些影响?

    常量池对jvm的性能影响基本上都是优化方向的:

    • 1、节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
    • 2、节省运行时间:比较字符串时,双等号比equals()快。对于两个引用变量,只用双等号判断引用是否相等,也就可以判断实际值是否相等。
    展开全文
  • Java 中的常量池

    2020-04-02 17:22:34
    两种常量池 一、静态常量池 要了解常量池,需要先对 jvm 的内存模型有一定的了解 1、jvm 内存模型 如上图所示,JVM 的内存区域基本分为五个部分 ...可以理解成class文件在内存中的存放位置,存放的就是...

    两种常量池

    一、静态常量池

    • 要了解常量池,需要先对 jvm 的内存模型有一定的了解

    1、jvm 内存模型

    在这里插入图片描述

    • 如上图所示,JVM 的内存区域基本分为五个部分
    本地方法栈
    • 调用操作系统方法所用的栈
    程序计数器
    • 就是指示字节码的执行行数的
    虚拟机栈
    • 就是执行 java 代码的栈
    • 一般用来存储对象,还包括常量池等
    方法区
    • 可以理解成class文件在内存中的存放位置,存放的就是已加载类的一些基本信息

    2、静态常量池

    • 所谓静态常量池就是class文件中的常量池,主要用于存放两大类常量:字面量符号引用量
    • 字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等
    • 符号引用包括了如下三种类型的常量:类和接口的全限定名、字段名称和描述符、方法名称和描述符

    二、动态常量池

    • 运行时常量池是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在虚拟机堆中,一般说的常量池就是指的这个
    • 运行时常量池最重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件中常量池的内容才能进入堆中的常量池,运行期间也可能将新的常量放入池中
    • String的intern()方法:查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池

    字符串常量池

    • 基本来讲我们接触最多的就是字符串常量池

    一、经典示例

    1、示例代码

    String s1 = "Hello";
    String s2 = "Hello";
    String s3 = "Hel" + "lo";
    String s4 = "Hel" + new String("lo");
    String s5 = new String("Hello");
    String s6 = s5.intern();
    String s7 = "H";
    String s8 = "ello";
    String s9 = s7 + s8;
              
    System.out.println(s1 == s2);  // true
    System.out.println(s1 == s3);  // true
    System.out.println(s1 == s4);  // false
    System.out.println(s1 == s9);  // false
    System.out.println(s4 == s5);  // false
    System.out.println(s1 == s6);  // true
    

    2、示例说明

    • s1 == s2 很好理解
    • s1 == s3 是编译器针对 + 操作符的优化,但是这样的优化仅限两边都是常量的时候
    • s1 == s4 new的部分不可预知,编译器无法进行优化
    • s1 == s9 s7和s8都是变量也是无法优化的
    • s4 == s5 都会产生新的对象存放于堆中,肯定不同的
    • s1 == s6 intern 方法就是先去常量池中找,这里显然是找到了

    二、几种特例

    • A和B明显就是两个常量而且是立即初始化的,所以这里编译器就可以进行优化了
    public static final String A = "ab"; // 常量A
    public static final String B = "cd"; // 常量B
    public static void main(String[] args) {
         String s = A + B;  // 将两个常量用+连接对s进行初始化 
         String t = "abcd";   
        if (s == t) {   
             System.out.println("s等于t,它们是同一个对象");   
         } else {   
             System.out.println("s不等于t,它们不是同一个对象");   
         }   
     } 
    //s等于t,它们是同一个对象
    
    • 这里虽然也是常量但是没有立即初始化,存在不确定性,编译器无法进行优化,因为编译器也不是万能的呀
    public static final String A; // 常量A
    public static final String B;    // 常量B
    static {   
         A = "ab";   
         B = "cd";   
     }   
     public static void main(String[] args) {   
        // 将两个常量用+连接对s进行初始化   
         String s = A + B;   
         String t = "abcd";   
        if (s == t) {   
             System.out.println("s等于t,它们是同一个对象");   
         } else {   
             System.out.println("s不等于t,它们不是同一个对象");   
         }   
     } 
    //s不等于t,它们不是同一个对象
    

    总结

    • 必须要关注编译期的行为,才能更好的理解常量池。
    • 运行时常量池中的常量,基本来源于各个class文件中的常量池。
    • 程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池

    补充

    • 整数类型包装类BYTE范围内(-128到127)是存放于常量池的
    • 浮点类型的包装类没有实现常量池技术

    参考文章:深入浅出java常量池

    展开全文
  • 在Java中,常量池的概念想必很多人都听说过。这也是面试中比较常考的题目之一。...本文是《好好说说Java中的常量池》系列的第一篇,先来介绍一下到底什么是Class常量池。 什么是Class文件 在Java代码的

    在Java中,常量池的概念想必很多人都听说过。这也是面试中比较常考的题目之一。在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的String面试题难倒了无数的开发者。所以说,常量池是Java体系中一个非常重要的概念。

    谈到常量池,在Java体系中,共用三种常量池。分别是字符串常量池Class常量池运行时常量池

    本文是《好好说说Java中的常量池》系列的第一篇,先来介绍一下到底什么是Class常量池。

    什么是Class文件

    Java代码的编译与反编译那些事儿中我们介绍过Java的编译和反编译的概念。我们知道,计算机只认识0和1,所以程序员写的代码都需要经过编译成0和1构成的二进制格式才能够让计算机运行。

    我们在《深入分析Java的编译原理》中提到过,为了让Java语言具有良好的跨平台能力,Java独具匠心的提供了一种可以在所有平台上都能使用的一种中间代码——字节码(ByteCode)。

    有了字节码,无论是哪种平台(如Windows、Linux等),只要安装了虚拟机,都可以直接运行字节码。

    同样,有了字节码,也解除了Java虚拟机和Java语言之间的耦合。这话可能很多人不理解,Java虚拟机不就是运行Java语言的么?这种解耦指的是什么?

    其实,目前Java虚拟机已经可以支持很多除Java语言以外的语言了,如Groovy、JRuby、Jython、Scala等。之所以可以支持,就是因为这些语言也可以被编译成字节码。而虚拟机并不关心字节码是有哪种语言编译而来的。

    Java语言中负责编译出字节码的编译器是一个命令是javac

    javac是收录于JDK中的Java语言编译器。该工具可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。

    如,我们有以下简单的HelloWorld.java代码:

    public class HelloWorld {
        public static void main(String[] args) {
            String s = "Hollis";
        }
    }

    通过javac命令生成class文件:

    javac HelloWorld.java

    生成HelloWorld.class文件:

    如何使用16进制打开class文件:使用 vim test.class ,然后在交互模式下,输入:%!xxd 即可。

    可以看到,上面的文件就是Class文件,Class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。

    要想能够读懂上面的字节码,需要了解Class类文件的结构,由于这不是本文的重点,这里就不展开说明了。

    读者可以看到,HelloWorld.class文件中的前八个字母是cafe babe,这就是Class文件的魔数(Java中的”魔数”

    我们需要知道的是,在Class文件的4个字节的魔数后面的分别是4个字节的Class文件的版本号(第5、6个字节是次版本号,第7、8个字节是主版本号,我生成的Class文件的版本号是52,这时Java 8对应的版本。也就是说,这个版本的字节码,在JDK 1.8以下的版本中无法运行)在版本号后面的,就是Class常量池入口了。

    Class常量池

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

    由于不同的Class文件中包含的常量的个数是不固定的,所以在Class文件的常量池入口处会设置两个字节的常量池容量计数器,记录了常量池中常量的个数。

    -w697

    当然,还有一种比较简单的查看Class文件中常量池的方法,那就是通过javap命令。对于以上的HelloWorld.class,可以通过

    javap -v  HelloWorld.class

    查看常量池内容如下:

    从上图中可以看到,反编译后的class文件常量池中共有16个常量。而Class文件中常量计数器的数值是0011,将该16进制数字转换成10进制的结果是17。

    原因是与Java的语言习惯不同,常量池计数器是从0开始而不是从1开始的,常量池的个数是10进制的17,这就代表了其中有16个常量,索引值范围为1-16。

    常量池中有什么

    介绍完了什么是Class常量池以及如何查看常量池,那么接下来我们就要深入分析一下,Class常量池中都有哪些内容。

    常量池中主要存放两大类常量:字面量(literal)和符号引用(symbolic references)。

    字面量

    前面说过,运行时常量池中主要保存的是字面量和符号引用,那么到底什么字面量?

    在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串;而有很多也对布尔类型和字符类型的值也支持字面量表示;还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法。

    以上是关于计算机科学中关于字面量的解释,并不是很容易理解。说简单点,字面量就是指由字母、数字等构成的字符串或者数值。

    字面量只可以右值出现,所谓右值是指等号右边的值,如:int a=123这里的a为左值,123为右值。在这个例子中123就是字面量。

    int a = 123;
    String s = "hollis";

    上面的代码事例中,123和hollis都是字面量。

    本文开头的HelloWorld代码中,Hollis就是一个字面量。

    符号引用

    常量池中,除了字面量以外,还有符号引用,那么到底什么是符号引用呢。

    符号引用是编译原理中的概念,是相对于直接引用来说的。主要包括了以下三类常量: * 类和接口的全限定名 * 字段的名称和描述符 * 方法的名称和描述符

    这也就可以印证前面的常量池中还包含一些com/hollis/HelloWorldmain([Ljava/lang/String;)V等常量的原因了。

    Class常量池有什么用

    前面介绍了这么多,关于Class常量池是什么,怎么查看Class常量池以及Class常量池中保存了哪些东西。有一个关键的问题没有讲,那就是Class常量池到底有什么用。

    首先,可以明确的是,Class常量池是Class文件中的资源仓库,其中保存了各种常量。而这些常量都是开发者定义出来,需要在程序的运行期使用的。

    在《深入理解Java虚拟》中有这样的表述:

    Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。关于类的创建和动态连接的内容,在虚拟机类加载过程时再进行详细讲解。

    前面这段话,看起来很绕,不是很容易理解。其实他的意思就是: Class是用来保存常量的一个媒介场所,并且是一个中间场所。在JVM真的运行时,需要把常量池中的常量加载到内存中。

    至于到底哪个阶段会做这件事情,以及Class常量池中的常量会以何种方式被加载到具体什么地方,会在本系列文章的后续内容中继续阐述。欢迎关注我的博客(http://www.hollischuang.com) 和公众号(Hollis),即可第一时间获得最新内容。

    另外,关于常量池中常量的存储形式,以及数据类型的表示方法本文中并未涉及,并不是说这部分知识点不重要,只是Class字节码的分析本就枯燥,作者不想在一篇文章中给读者灌输太多的理论上的内容。感兴趣的读者可以自行Google学习,如果真的有必要,我也可以单独写一篇文章再深入介绍。

    参考资料

    《深入理解java虚拟机》 《Java虚拟机原理图解》 1.2.2、Class文件中的常量池详解(上)

    展开全文
  • Java中的常量池(字符串常量池、class常量池和运行时常量池) 转载。 https://blog.csdn.net/zm13007310400/article/details/77534349 简介: 这几天在看...
  • 转载自 好好说说Java中的常量池之Class常量池 在Java中,常量池的概念想必很多人都听说过。这也是面试中比较常考的题目之一。在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的...
  • java中的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池。 java常量池简介:java常量池中保存了一份在编译期间就已确定的数据。它里面包括final...
  • java中的常量池jdk7和之前有什么变化 有同学可以解释一下吗
  • 关于java中的常量池

    2021-04-26 16:06:16
    class字节码中的常量池:存在于硬盘上。主要存放字面量和符号引用。 运行时常量池:方法区的一部分。我们常说的常量池,就是指这一块区域。 字符串常量池:存在于堆区。这个常量池在JVM层面就是一个StringTable...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,744
精华内容 3,497
关键字:

java中的常量池

java 订阅