`
布鲁斯
  • 浏览: 4031 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Constant Pool常量池的概念

阅读更多
Constant Pool常量池的概念

在讲到String的一些特殊情况时,总会提到String Pool或者Constant Pool,但是我想很多人都不太
明白Constant Pool到底是个怎么样的东西,运行的时候存储在哪里,所以在这里先说一下Constant Pool的内容.
String Pool是对应于在Constant Pool中存储String常量的区域.习惯称为String Pool,也有人称为
String Constant Pool.好像没有正式的命名??
在java编译好的class文件中,有个区域称为Constant Pool,他是一个由数组组成的表,类型
为cp_info constant_pool[],用来存储程序中使用的各种常量,包括Class/String/Integer等各
种基本Java数据类型,详情参见The Java Virtual Machine Specification 4.4章节.

对于Constant Pool,表的基本通用结构为:
cp_info {
        u1 tag;
        u1 info[];
}

tag是一个数字,用来表示存储的常量的类型,例如8表示String类型,5表示Long类型,info[]根据
类型码tag的不同会发生相应变化.
对于String类型,表的结构为:
CONSTANT_String_info {
        u1 tag;
        u2 string_index;
}
tag固定为8,string_index是字符串内容信息,类型为:
CONSTANT_Utf8_info {
        u1 tag;
        u2 length;
        u1 bytes[length];
}
tag固定为1,length为字符串的长度,bytes[length]为字符串的内容.
(以下代码在jdk6中编译)
为了详细理解Constant Pool的结构,我们参看一些代码:
    String s1 = "sss111";
    String s2 = "sss222";
    System.out.println(s1 + " " + s2);
由于"sss111"和"sss222"都是字符串常量,在编译期就已经创建好了存储在class文件中.
在编译后的class文件中会存在这2个常量的对应表示:
08 00 11 01 00 06 73 73 73 31 31 31 08 00 13 01 ; ......sss111....
00 06 73 73 73 32 32 32                         ; ..sss222
根据上面说的String常量结构,我们分析一下
开始的08为CONSTANT_String_info结构中的tag,而11应该是它的相对引用,01为
CONSTANT_Utf8_info的tag,06为对应字符串的长度,73 73 73 31 31 31为字符串对
应的编码,接着分析,会发现后面的是对应"sss222"的存储结构.

经过上面分析,我们知道了11和13是两个字符串的相对引用,就可以修改class文件
来修改打印的内容,把class文件中的
00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 12 4D
改成
00 6E 00 04 00 03 00 00 00 24 12 10 4C 12 10 4D
程序就会输出sss111 sss111,而不是和原程序一样输出sss111 sss222,因为我
们把对"sss222"的相对引用12改成了对"sss111"的相对引用10.

------------分割线
public class Test {
    public static void main(String[] args) {
        String s1 = "sss111";
        String s2 = "sss111";
    }
}
在上面程序中存在2个相同的常量"sss111",对于n个值相同的String常量,在Constant Pool中
只会创建一个,所以在编译好的class文件中,我们只能找到一个对"sss111"的表示:
000000abh: 08 00 11 01 00 06 73 73 73 31 31 31             ; ......sss111

在程序执行的时候,Constant Pool会储存在Method Area,而不是heap中.
另外,对于""内容为空的字符串常量,会创建一个长度为0,内容为空的字符串放到Constant Pool中,
而且Constant Pool在运行期是可以动态扩展的.

关于String类的说明
1.String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能
再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的(immutable).
2.String类有一个特殊的创建方法,就是使用""双引号来创建.例如new String("i am")实际创建了2个
String对象,一个是"i am"通过""双引号创建的,另一个是通过new创建的.只不过他们创建的时期不同,
一个是编译期,一个是运行期!
3.java对String类型重载了+操作符,可以直接使用+对两个字符串进行连接.
4.运行期调用String类的intern()方法可以向String Pool中动态添加对象.
String的创建方法一般有如下几种
1.直接使用""引号创建.
2.使用new String()创建.
3.使用new String("someString")创建以及其他的一些重载构造函数创建.
4.使用重载的字符串连接操作符+创建.
例1
    /*
    * "sss111"是编译期常量,编译时已经能确定它的值,在编译
    * 好的class文件中它已经在String Pool中了,此语句会在
    * String Pool中查找等于"sss111"的字符串(用equals(Object)方法确定),
    * 如果存在就把引用返回,付值给s1.不存在就会创建一个"sss111"放在
    * String Pool中,然后把引用返回,付值给s1.
    *
    */
    String s1 = "sss111";
    //此语句同上
    String s2 = "sss111";
    /*
    * 由于String Pool只会维护一个值相同的String对象
    * 上面2句得到的引用是String Pool中同一个对象,所以
    * 他们引用相等
    */
    System.out.println(s1 == s2); //结果为true

例2
    /*
    * 在java中,使用new关键字会创建一个新对象,在本例中,不管在
    * String Pool中是否已经有值相同的对象,都会创建了一个新的
    * String对象存储在heap中,然后把引用返回赋给s1.
    * 本例中使用了String的public String(String original)构造函数.
    */
    String s1 = new String("sss111");
   
    /*
     * 此句会按照例1中所述在String Pool中查找
     */
    String s2 = "sss111";
   
    /*
     * 由于s1是new出的新对象,存储在heap中,s2指向的对象
     * 存储在String Pool中,他们肯定不是同一个对象,只是
     * 存储的字符串值相同,所以返回false.
     */
    System.out.println(s1 == s2); //结果为false

例3
    String s1 = new String("sss111");
    /*
    * 当调用intern方法时,如果String Pool中已经包含一个等于此String对象
    * 的字符串(用 equals(Object)方法确定),则返回池中的字符串.否则,将此
    * String对象添加到池中,并返回此String对象在String Pool中的引用.
    */
    s1 = s1.intern();
   
    String s2 = "sss111";
   
    /*
     * 由于执行了s1 = s1.intern(),会使s1指向String Pool中值为"sss111"
     * 的字符串对象,s2也指向了同样的对象,所以结果为true
     */
    System.out.println(s1 == s2);

例4
    String s1 = new String("111");
    String s2 = "sss111";
   
    /*
    * 由于进行连接的2个字符串都是常量,编译期就能确定连接后的值了,
    * 编译器会进行优化直接把他们表示成"sss111"存储到String Pool中,
    * 由于上边的s2="sss111"已经在String Pool中加入了"sss111",
    * 此句会把s3指向和s2相同的对象,所以他们引用相同.此时仍然会创建出
    * "sss"和"111"两个常量,存储到String Pool中.
    */
    String s3 = "sss" + "111";
   
    /*
     * 由于s1是个变量,在编译期不能确定它的值是多少,所以
     * 会在执行的时候创建一个新的String对象存储到heap中,
     * 然后赋值给s4.
     */
    String s4 = "sss" + s1;
   
    System.out.println(s2 == s3); //true
    System.out.println(s2 == s4); //false
    System.out.println(s2 == s4.intern()); //true

例5
这个是The Java Language Specification中3.10.5节的例子,有了上面的说明,这个应该不难理解了
    package testPackage;
    class Test {
            public static void main(String[] args) {
                    String hello = "Hello", lo = "lo";
                    System.out.print((hello == "Hello") + " ");
                    System.out.print((Other.hello == hello) + " ");
                    System.out.print((other.Other.hello == hello) + " ");
                    System.out.print((hello == ("Hel"+"lo")) + " ");
                    System.out.print((hello == ("Hel"+lo)) + " ");
                    System.out.println(hello == ("Hel"+lo).intern());
            }
    }
    class Other { static String hello = "Hello"; }
    package other;
    public class Other { static String hello = "Hello"; }
输出结果为true true true true false true,请自行分析!

结果上面分析,总结如下:
1.单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中.
2.使用new String("")创建的对象会存储到heap中,是运行期新创建的.
3.使用只包含常量的字符串连接符如"aa" + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中.
4.使用包含变量的字符串连接符如"aa" + s1创建的对象是运行期才创建的,存储在heap中.
6.使用"aa" + s1以及new String("aa" + s1)形式创建的对象是否加入到String Pool中我不太确定,可能是必须
调用intern()方法才会加入,希望高手能回答 @_@

还有几个经常考的面试题:
1.
String s1 = new String("s1") ;
String s2 = new String("s1") ;
上面创建了几个String对象?
答案:3个 ,编译期Constant Pool中创建1个,运行期heap中创建2个.

2.
String s1 = "s1";
String s2 = s1;
s2 = "s2";
s1指向的对象中的字符串是什么?
答案: "s1"
0
0
分享到:
评论
1 楼 mercyblitz 2010-04-01  
排版会更好,哈哈。

JVM规范里面多少提了,楼主幸苦了。

相关推荐

    java 内存中 堆、栈、常量池、方法区的总结

    在Java内存管理中,堆(Heap)、栈(Stack)、常量池(Constant Pool)和方法区(Method Area)是四个核心概念,它们在Java程序运行时扮演着不同的角色。 首先,方法区是用来存放类的信息、常量、静态变量等数据的...

    Java堆,栈和常量池详解

    常量池 (Constant Pool) 常量池是一块用于存放编译期生成的各种字面量和符号引用的内存区域,它存在于方法区中。Java语言规范规定,常量池是一个表结构,包含一系列的常量定义。每一个类或者接口都会在其方法区有...

    什么是字符串常量池?Java开发Java经验技巧共6页.p

    在Java编程语言中,字符串常量池(String Constant Pool)是一个重要的概念,它与程序的内存管理和性能优化密切相关。理解这个概念对于任何Java开发者来说都至关重要。字符串常量池是Java虚拟机(JVM)在运行时为...

    java入门教程:数据类型_运行时常量池.docx

    Java编程语言的基础知识中,数据类型和运行时常量池(Runtime Constant Pool)是两个关键概念。数据类型决定了变量可以存储的值的种类和大小,而运行时常量池则是Java虚拟机(JVM)内存模型中的一个重要组成部分。 ...

    Java中的堆、栈和常量池_.docx

    5. 常量池(Constant Pool): 常量池存储字符串常量和基本类型的常量(用public static final修饰)。在Java 8之前,字符串常量池位于方法区,从Java 8开始,字符串常量池被移到了堆中。常量池允许字符串的复用,...

    java堆-栈-堆栈常量池的区别,史上最全总结

    3. 常量池(Constant Pool): 常量池是Java内存模型中的一个重要组成部分,它存储字符串常量和基本类型的final常量。在编译阶段,所有的字符串文字会被放入常量池,以实现对象的共享,从而节省内存和提高运行效率。...

    Java常量池理解与总结

    再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计...

    解析Java中的String对象的数据类型 字符串

    常量池(constant pool)是Java中的一个概念,它指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。常量池包括了关于类、方法、接口等中的常量,也包括字符串常量。例如String s0="kvill";,String ...

    JVM 50 道面试题及答案.docx

    3. 类文件常量池: constant_pool,JVM 定义的概念。 五、类加载 JDK 1.8 开始,永久代被移出,使用元空间(Metaspace),永久代剩余内容移至元空间,元空间直接在本地内存分配。 六、JVM 调优 可以使用以下命令...

    2023JVM面试题及答案-共50道.docx

    - 类文件常量池(Constant Pool)是JVM规范中的概念,包含在.class文件中。 3. **动态年龄判断**: - 这涉及到JVM的垃圾收集策略。例如,Survivor区的目标使用率(默认50%),当同一年龄的对象大小总和达到...

    Java内存分配

    5. **常量池(Constant Pool)**:常量池存储编译期间确定的各种常量,包括基本类型、对象引用、字符串、符号引用等。在运行时,这些常量存储在方法区中,JVM为每个加载的类维护一个常量池。 6. **非RAM存储(Non-...

    深入理解JavaString#intern()内存模型Ja

    `intern()`方法是一个非常特殊的函数,它将字符串常量池(String Constant Pool)的概念引入到我们的讨论中。 字符串常量池是Java虚拟机(JVM)内存模型的一部分,位于堆内存的常量池区域。在Java 6及之前,这个池...

    String对象的内存分析

    3. 常量池(Constant Pool):是堆内存中的一部分,用来存储字符串常量和基本类型常量,以及`public static final`修饰的变量。常量池中的数据可以被多个对象共享。 4. 静态存储:用于存储`static`定义的成员,这些...

    Java Class文件格式详解1

    常量池的每个条目都有一个`tag`字段来标识其类型,比如CONSTANT_Class_info、CONSTANT_Integer_info等。这种设计允许通过索引而非重复数据来节省空间。例如,CONSTANT_Integer_info用于存储int类型的常量,而...

    string中的常见问题

    首先,我们需要理解字符串常量池(String Constant Pool)的概念。当程序中通过直接赋值的方式定义一个字符串时,例如 `String a = "ab";` 这样的字符串会存储在字符串常量池中。如果在程序中再次尝试创建一个相同的...

Global site tag (gtag.js) - Google Analytics