JVM常量池

134 阅读4分钟

常量池

常量池分静态常量池、运行时常量池、字符串常量池和八种基本数据类型对象池。

1、静态常量池:存在于class文件中。

这是一个class文件的16进制结构里面包含了类的版本、字段、方法、接口等描述信息外,还有一项就是常量池,用于存放编译期生成的各种
关于字节码方法表与属性表详解可以看这个链接blog.csdn.net/weixin_3089… image.png

字面量:指由字母、数字等组成的字符串或数值常量。字面量只出现在等号右边。下面代码中1,"abc","1ba2"就是字面量

int a = 1
int b = "abc"
int c = "1ba2" 符号引用:相对于直接引用来说的,主要包括以下三类常量:

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

这些常量池现在都是静态的,当被加载到内存中后,才会有对应的内存地址,也就变成了运行时常量池,符号引用转变为内存区域代码的直接引用(动态链接) image.png

2、运行时常量池:在class文件加载到内存中后,常量池的内容保存在了方法区中,成为了运行时常量池。

3、字符串常量池:在堆中。字符串在分配的时候,和其他对象一样,会产生时间和空间的消耗,频繁的创建字符串,对性能的影响很大。所以JVM在实例化字符串常量时,单独划了一块字符串常量池来存放。创建字符串常量时,先看字符串常量池中是否存在,存在就直接返回引用实例,不存在则实例化该字符串并放入字符串常量池中。

  1. test和test2都是运行时常量池中的实例
public static void test1(){

    String test = "abc";

    String test2 = "abc";

    System.out.println(test==test2);//true
}

image.png

  1. 这种编译器会进行优化,在编译阶段会把"a" "b" "c"优化成字符串"abc",然后在类加载阶段会把字符串"abc"字符串纳入到运行时常量池中。test和test2都是运行时常量池中的实例
public static void test1(){

    String test = "a"+"b"+"c";

    String test2 = "abc";

    System.out.println(test==test2);//true

}

image.png

  1. test是运行时常量池中的实例,test2是堆中的对象
public static void test1(){

    String test = "abc";

    String test2 = new String("abc");

    System.out.println(test==test2);//false

}

image.png

  1. test和test2分别是堆中的对象
public static void test1(){

    String test = new String("abc");

    String test2 = new String("abc");

    System.out.println(test==test2);//false

}

image.png

  1. test和test2分别是堆中的对象
public static void test1(){

    String test = new String("c")+"ab";

    String test2 = new String("abc");

    System.out.println(test==test2);//false

}

image.png

  1. 第六种 String.intern()方法,若运行时常量池没有该对象 就直接把该字符串丢进运行时常量池,若有,返回运行时常量池的引用。test是堆中的对象,test3和test2是运行时常量池中的实例。
public static void test1(){

    String test = "ab" + new String("c");

    String test2 = "abc";

    String test3 = test.intern();

    System.out.println(test3==test);//false

}

image.png

  1. test产生后,常量池中产生"abc",test3和test2是运行时常量池中的实例。
public static void test1(){

    String test = "ab" + new String("c");

    String test2 = "abc";

    String test3 = test.intern();

    System.out.println(test3==test2);//true

}

image.png

4、八种基本数据类型的包装类和对象池:在堆中。Byte,Short,Integer,Long,Character,Boolean实现了,Float和Double则没有实现。另外,Byte,Short,Integer,Long,Character这5种只有在小于128时才能使用对象池。

  • Integer
//小于127,执行了Integer.valueOf(127),用了IntegerCache对象池
Integer a1 = 127;
Integer a2 = 127;
System.out.println(a1 == a2);//true

//大于127,不用IntegerCache对象池
Integer a3 = 128;
Integer a4 = 128;
System.out.println(a3 == a4);//false
  • 通过Integer的valueOf方法,可以很清楚的看到只有在值小于等于127时,才用到对象池。
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}
}
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
  • 需要注意的是,使用new Integer生成新对象是不使用对象池的
Integer a5 = new Integer(127);
Integer a6 = new Integer(127);
System.out.println(a5 == a6);//false
  • Double和Float不使用对象池
Double a7 = 1.2;
Double a8 = 1.2;
System.out.println(a7 == a8);//false

Float a9 = 3.0f;
Float a10 = 3.0f;
System.out.println(a9 == a10);//false