java学习——浅谈Java常量池

一、概述

  • 常量池:编译期被确定,*.class文件中的一部分,包含字面量(Literal)和符号引用(Symbolic Reference)。
  • 字面量:文本字符串、声明为final的常量值(int/long/double...)等。
  • 符号引用:类和接口的完全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符。
  • 运行时常量池:方法区的一部分,jvm在完成类装载操作后,将class文件中的常量池载入内存并保存在方法区中。
  • JDK1.6之前字符串常量池位于方法区。
    JDK1.7字符串常量池已经被移至堆。
    JDK1.8字符串常量池位移至元空间。

二、字符串常量池

字符串常量池属于运行时常量池的一部分

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

java中==比较的是内存地址

  1. s1 == s2 (true),s1、s2赋值时均使用的字符串字面量"Hello",在编译期间,这种字面量会直接放入class文件的常量池中,载入运行时常量池后,s1、s2指向的是同一个内存地址。
  2. s1 == s3 (true),s3虽然是动态拼接出来的字符串,但所有参与拼接的部分都是已知的字面量,在编译期间,这种拼接会被优化,因此String s3 = "Hel" + "lo";在class文件中被优化成String s3 = "Hello";,所以s1 == s3成立。
  3. s1 == s4 (false),s4虽然也是拼接出来的,但new String("lo")这部分不是已知字面量,编译器不会优化,必须等到运行时才可以确定结果,所以s4指向堆中某个地址。


    s4.jpg
  4. s1 == s9 (false),s9是s7、s8两个变量拼接,都是不可预料的,编译器不作优化,运行时拼接成新字符串存于堆中某个地址。


    s9.png
  5. s4 == s5 (false),二者都在堆中,但地址不同。
  6. s1 == s6 (true),这两个相等完全归功于intern()方法,s5在堆中,内容为"Hello" ,intern方法会尝试将"Hello"字符串添加到常量池中,并返回其在常量池中的地址,因为常量池中已经有了"Hello"字符串,所以intern方法直接返回地址;而s1在编译期就已经指向常量池了,因此s1和s6指向同一地址,相等。
  • 以上所讲仅涉及字符串常量池,实际上还有整型常量池、浮点型常量池等等,但都大同小异
  • 数值类型的常量池不可以手动添加常量,程序启动时常量池中的常量就已经确定了,比如整型常量池中的常量范围:-128~127,只有这个范围的数字可以用到常量池。

三个非常重要的结论:

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

三、常量池溢出

/**
 * jdk1.6 -XX:MaxPermSize=5M OutOfMemoryError: PermGen space
 * jdk1.7 -Xmx5M OutOfMemoryError: Java heap space
 * jdk1.8 -Xmx5M OutOfMemoryError: GC overhead limit exceeded
 * jdk1.8 -XX:MaxMetaspaceSize=2M OutOfMemoryError: Metaspace
 */
public class ConstantPoolOOM {

  public static void main(String[] args) throws InterruptedException {
    List<String> list = new ArrayList<String>();
    int index = 0;
    while (true) {
      list.add(String.valueOf(index++).intern());
    }
  }
}
  • jdk1.6运行时常量池在方法区中,设置-XX:MaxPermSize=5M,导致OutOfMemoryError: PermGen space
  • jdk1.7运行时常量池在堆中,仅仅保存常量的引用,常量对象在堆中,设置-Xmx5M,导致OutOfMemoryError: Java heap space
  • jdk1.8运行时常量池在元空间中,仅仅保存常量的引用,常量对象在堆中,
    设置-XX:MaxMetaspaceSize=2M,导致OutOfMemoryError: Metaspace
    设置-Xmx5M,导致OutOfMemoryError: GC overhead limit exceeded

四、jdk1.6和1.7+常量池的差异

/**
 * jdk1.6 false false
 * jdk1.7+ true false
 */
public class ConstantPoolTest {

  public static void main(String[] args) {
    String str1=new StringBuilder("计算机").append("软件").toString();
    System.out.println(str1.intern()==str1);
    String str2=new StringBuilder("ja").append("va").toString();
    System.out.println(str2.intern()==str2);
  }
}
  1. 上面代码在jdk1.6中执行会打印两个false
    1.6中intern()方法会把首次出现的字符串实例复制到运行时常量池(方法区)中,并返回方法区中这个实例的地址,而str1 str2的地址都在堆中,所以两次都打印false。
  2. 在jdk1.7种执行第一个会打印true,第二个会打印false
    1.7以后版本intern()方法会把首次出现的字符串实例的引用运行时常量池中,
    "计算机软件"是首次出现的字符串,会把str1的引用存入运行时常量池,所以str1.intern()返回的就是str1的引用,打印true。
    "java"在创建str2对象之前已出现过,运行时常量池中已经存在,所以打印false。

参考资料:
http://www.cnblogs.com/iyangyuan/p/4631696.html
https://segmentfault.com/a/1190000010412582

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269