运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;

否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。

在JDK1.6及之前版本中,由于常量池分配在永久代中(即方法区),我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量,

注意,JDK1.7开始逐步开始“去永久代”。代码如下所示:

package jvm;

import java.util.ArrayList;

import java.util.List;

/*

* VM Args: -XX:PermSize=10m -XX:MaxPermSize=10m

*/

public class RuntimeConstantPoolOOM {

public static void main(String[] args) {

// 使用List保持着常量池引用,避免Full GC回收常量池行为

List list = new ArrayList();

int i = 0;

while (true) {

list.add(String.valueOf(i++).intern());

}

}

}

注意,VM Args为配置VM的参数,在下图所示中配置:

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

at java.lang.String.intern(Native Method)

at jvm.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:16)

从运行结果中可以看到,运行时常量池溢出,在OutOfMemoryError后面跟随的提示信息是“PermGen space”,说明运行时常量池属于方法区(HotSpot虚拟机中的永久代)的一部分。但是使用JDK1.7运行这段程序不会得到相同的结果,而是出现以下的提示信息,这是因为这两个参数已经不在JDK1.7中使用了。

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=10m; support was removed in 8.0

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=10m; support was removed in 8.0

如果在JDK1.7中运行RuntimeConstantPoolOOM.java程序,while循环将一直运行下去,但是,while循环并不是始终运行下去,直到系统中堆内存用完为止,一般需要过好长时间才会出现,不过笔者并没有在本地测试。因为在JDK1.7中常量池存储的不再是对象,而是对象引用,真正的对象是存储在堆中的。把RuntimeConstantPoolOOM.java运行时的VM参数改为如下所示:

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

运行程序后结果:

出现异常提示信息:java.lang.OutOfMemoryError: GC overhead limit exceeded,这里没有提示说堆还是持久代有问题,虚拟机只是告诉你你的程序花在垃圾回收上的时间太多了,却没有什么见效。默认的话,如果你98%的时间都花在GC上并且回收了才不到2%的空间的话,虚拟机才会抛这个异常。这是一个快速失败的安全保障的很好的实践。从运行结果中可以看出, 我们限定了堆的大小后,程序很快就运行异常了,异常信息和之前设想的一样,也就是常量池存储的不再是对象,而是对象引用,真正的对象是存储在堆中的。

关于JDK1.7字符串常量池的实现问题,这里还可以引申一个更有意义的影响,如以下代码所示:

public classStringInternStudyDemo {public static voidmain(String[] args) {

printJdkVersion();

testAndPrintResult("计算机", "软件");

testAndPrintResult("ja", "va");

testAndPrintResult("ma", "in");

}private static voidtestAndPrintResult(String prefix, String suffix) {

String str3= newStringBuilder(prefix).append(suffix).toString();

System.out.println(str3.intern()==str3);

}private static voidprintJdkVersion() {

String javaVersion= "java.version";

System.out.println(javaVersion+ ":" +System.getProperty(javaVersion));

}

}

JDK1.8下的执行结果:

java.version:1.8.0_65true

false

false

JDK1.6下的执行结果:

java.version:1.6.0_45false

false

false

这段代码在JDK1.6中运行,会得到两个false,而在JDK1.7中运行,会得到一个true和一个false。

产生差异的原因是:

在JDK1.6中,intern()方法会把首次遇到的字符串复制到永久代中,返回的也是永久代中这个字符串的引用,而由StringBuilder创建的字符串实例在Java堆中,所以必然不是同一个引用,将返回false。

而JDK1.7(以及部分其他虚拟机,例如JRockit)的intern()实现不会再复制实例,而是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串是同一个。

对str2比较返回false,

是因为"java"字符串在执行StringBuilder()之前就已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”原则,而“计算机软件”这个字符串则是首次出现的,因此返回true。

如果在Hello.java中添加如下代码的话,返回的结果也是false,证明"main"字符串之前也出现过了。

String str3 = new StringBuilder("ma").append("in").toString();

System.out.println(str3.intern() == str3);

参考

1、《深入理解Java虚拟机》 2.4.3章节

http://www.cnblogs.com/luoxn28/p/5425425.html

java 常量池溢出_Java方法区和运行时常量池溢出问题分析(转)相关推荐

  1. java常量池方法区_Java方法区和运行时常量池溢出问题分析

    运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等. String.intern()是一个native方法,它的作用是:如果字符串常量池 ...

  2. Java方法区和运行时常量池溢出问题分析(转)

    运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等. String.intern()是一个native方法,它的作用是:如果字符串常量池 ...

  3. 第二章:方法区和运行时常量池溢出

    由于运行时常量池属于方法区的一部分,因此两个区域放在一块执行. String.intern()是一个Native方法,它的作用是如果字符串常量池中已经包含了此String对象的字符串,则返回代表池中这 ...

  4. [转载] java常量池-字符串常量池、class常量池和运行时常量池

    参考链接: 如何在Java中初始化和比较字符串 原文链接:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool ...

  5. java永久区_Java方法区和永久代

    目前有三大Java虚拟机:HotSpot,oracle JRockit,IBM J9. JRockit是oracle发明的,用于其WebLogic服务器,IBM JVM是IBM发明的用于其Websph ...

  6. 3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记)

    3.JVM内存分配 3.1.内存分配概述 3.2.内存分配–Eden区域 3.3.内存分配–大对象直接进老年代 3.3.1.背景 3.3.2.解析 3.4.内存分配–长期存活的对象进去老年代 3.5. ...

  7. Java虚拟机(九)——方法区

    文章目录 堆.栈.方法区的交互关系 线程是否共享: 三者的交互关系: 方法区的理解 方法区的位置 基本理解: 设置方法区大小 JDK7以前 JDK8及以后: 方法区的内部结构 方法区存储什么 类型信息 ...

  8. 字符串常量池、class常量池和运行时常量池

    原文链接:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/ 在java的内存分配中,经常听到很多关于常量 ...

  9. class 常量池、字符串常量池和运行时常量池的区别

    文章目录 class 常量池.字符串常量池和运行时常量池的区别 概念 常量池(Constant Pool) 字符串常量池(String Pool) 运行时常量池( Runtime Constant P ...

最新文章

  1. 自学python要下载什么软件-Python学习需要安装的工具
  2. Acwing104. 货仓选址:贪心(绝对值不等式)
  3. 海尔智家财报优于预期,下注未来正当其时
  4. arm集群服务器_百度云智峰会发布ARM私有云新品,加速企业在移动端上的数字化转型...
  5. (转)常用正则表达式
  6. 浅谈一致性Hash原理及应用
  7. 洛谷——P1010 [NOIP1998 普及组] 幂次方
  8. HubbleDotNet 基本语法
  9. js手机号批量滚动抽奖代码实现
  10. 教你如何制作浪漫的表白网站 七夕情人节表白网页在线制作(多种款式)
  11. 运维基础知识总结(环境搭建篇)
  12. 通信系统、基本原理概念
  13. 手把手教你ZPanelX 10.0.0在window下的汉化
  14. 关于微信小程序中的取整
  15. 自己感觉比较不错的美剧(记录自己追剧的岁月)
  16. vConsole:H5控制台调试工具
  17. Mask Scoring R-CNN
  18. Jasper问题总结与解决方法~
  19. 活动目录是做什么用的
  20. 一文轻松看懂线性回归分析的交互作用!

热门文章

  1. 【英语学习】4000 Words 【V1】【U02】The Laboratory
  2. 【英语学习】【WOTD】resurrection 释义/词源/示例
  3. Intel 64/x86_64/IA-32/x86处理器 - 通用指令(8) - 杂项指令 用户态扩展状态指令 随机数生成指令
  4. 运算器(串行加法器和并行加法器,ALU)
  5. 性能测试、 障碍条件和回滚
  6. mysql 设置 0、1 用什么数据类型_MySQL数据库教程-数据表字段约束
  7. CLion 下使用 Valgrind 内存检查 [Mac;Linux;Window多平台]
  8. Web服务器处理连接请求的四种架构方式
  9. SecureCRT session 的迁移
  10. glibc库详解及与POSIX,system V这些库之间关系的说明