java常量池_java常量池 - Mazin的个人空间 - OSCHINA - 中文开源技术交流社区

首先,我们来看看常量池的概念,常量池可以分成3类:

1.静态常量池:也就是class文件中的常量池,一般用来存放class文件中定义的一些常量,包括类和接口的全限定名,字段的名称和描述符以及方法和名称和描述符。

2.字符串常量池:即class文件中定义的String类型,这个常量池就存在与静态常量池中。

3.运行时常量池:我们平时所说的常量池就是这个了,它存放在方法区中,当所有的class文件都加载完成之后,会将多有的常量都放入其中,以供开发使用。

那么,哪些数据会被存放到常量池中呢?除了上面提到的String类型,8中基本数据类型中有6中都会存放如常量池中,下面我们来看一段代码:

public class ConstantTest {

public static void main(String[] args) {

//验证Integer类型

Integer i1 = 127;

Integer i2 = 127;

System.out.println(i1==i2);//true

Integer i3 = 128;

Integer i4 = 128;

System.out.println(i3==i4);//false

//验证Long类型

Long l1 = 127L;

Long l2 = 127L;

System.out.println(l1==l2);//true

Long l3 = 128L;

Long l4 = 128L;

System.out.println(l3==l4);//false

//验证Byte类型

Byte b1 = 127;

Byte b2 = 127;

System.out.println(b1==b2);//true

//验证short类型

Short s1 = 127;

Short s2 = 127;

System.out.println(s1==s2);//true

Short s3 = 128;

Short s4 = 128;

System.out.println(s3==s4);//false

//验证Character类型

Character c1 = 'a';

Character c2 = 'a';

System.out.println(c1==c2);//true

Character c3 = '你';

Character c4 = '你';

System.out.println(c3==c4);//false

//验证boolean类型

Boolean bo1 = true;

Boolean bo2 = true;

System.out.println(bo1==bo2);//true

//验证double类型

Double d1 = 1.2;

Double d2 = 1.2;

System.out.println(d1==d2);//false

//验证float类型

Float f1 = 1.2f;

Float f2 = 1.2f;

System.out.println(f1==f2);//false

}

}

通过以上的代码和运行结果,我们可以知道,8种数据类型的包装类中,除了Double和Float,其它6种都会进入常量池,但是数值范围在-128到127之间。

下面我们再来看看String类型:

public class ConstantTest {

public static void main(String[] args) {

String s1 = "java";

String s2 = new String("java");

}

}

这两行代码都是将”java“这个字符串赋值,但是在内存空间的角度来看,它们是有区别的,看下面这张图:

057beb6fb01043156c74d2c352c8896a.png

图中的两条绿线比较好理解,就是直接赋值的时候,是去常量池中查看是否有该对象,如果没有,就创建并返回其地址引用,如果有,就直接返回地址引用,而通过new的方法创建的String对象是存放在堆内存种的,值得注意的是那条红线,在执行new String的时候,也会去常量池中查看该字符串是否已经存在,如果不存在,那么就在常量池中创建一个。所以如果问new String("java")这句代码产生了几个对象,如果常量池中已经有这个对象了,那么只会产生一个,如果没有,那么会产生两个。

我们再来看看下面这段代码:

public class ConstantTest {

public static void main(String[] args) {

String s1 = "hello";

String s2 = "world";

String s3 = "hello"+"world";

String s4 = s1+s2;

String s5 = "helloworld";

System.out.println(s3 == s5);//true

System.out.println(s4 == s5);//false

}

}

为什么会出现这种结果呢?我们来分析一下,前两行代码执行之后,会向常量池中添加"hello"和 "world"这两个常量,由于jvm的编译时优化,当两个常量进行相加的时候,会将这个组成的新的常量添加到常量池中,并将引用返回,所以第三行代码就是向常量池中添加了"helloworld",并将地址值赋给 s3,所以第一个为true,那么第二个为什么为false呢?因为第四行代码是两个变量相加,变量具有不确定性,所以jvm的编译优化不会起作用,所以第二个是false。

再来说说常量池的位置,在jdk1.6及之前的版本中,常量池位于perm区,也就是我们常说的方法区,这个区和堆是没有关系的,它们是分开的,而到了1.7版本之后,常量池就转放到heap中了,而且值得注意的是,在1.8版本后,perm区也被取消了,取代的是元空间(metaspace)。

下面这段代码是在jdk1.8环境下运行的:

public class InternTest {

public static void main(String[] args) {

ArrayList list = new ArrayList();

for (int i = 0; i < 500000000; i++) {

String intern = String.valueOf(i).intern();

list.add(intern);

}

}

}

我们将jvm堆内存的大小设置小一点:

-Xmx1m -Xms1m -XX:-UseGCOverheadLimit

这里注意一定要使用list将数据添加进去,否则会由于GC而不会出现内存溢出,运行结果如下

e941182654878e124751cc7289885400.png

发现是堆内存溢出了,说明在1.8的时候,常量池已经被移到了heap中。

到这里,常量池的基本内容就差不多了,不过既然是运行时常量池,就必定牵扯到动态的向其中添加数据,开发中常见的方法就是String的intern方法,想要深入了解这个方法,可参考String的intern方法的深入分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值