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

String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含了一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。在JDK1.6及之前版本中,由于常量池分配在永久代中(即方法区),我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量,注意,JDK1.7开始逐步开始“去永久代”。代码如下所示:

packagejvm;importjava.util.ArrayList;importjava.util.List;/** VM Args: -XX:PermSize=10m -XX:MaxPermSize=10m*/

public classRuntimeConstantPoolOOM {public static voidmain(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.0Java 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字符串常量池的实现问题,这里还可以引申一个更有意义的影响,如以下代码所示:

packagejvm;public classHello {public static voidmain(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);

}

}

这段代码在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章节

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

  1. java运行时读取注解_Java自定义注解和运行时靠反射获取注解

    转:1.Annotation的工作原理: JDK5.0中提供了注解的功能,允许开发者定义和使用自己的注解类型.该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰 ...

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

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

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

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

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

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

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

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

  6. java 方法 时间_Java 方法

    方法的概念 方法就是将功能重复的代码封装成一段独立的小程序,通过调用方法的方式以提高代码的复用性(减少代码重复) int year = 2020; if ( year % 4 == 0 &&a ...

  7. java中重载 参数顺序_Java方法中的参数太多,第4部分:重载

    java中重载 参数顺序 期望将过多的参数传递给Java方法的问题之一是,该方法的客户端很难确定它们是否以适当的顺序传递了适当的值. 在以前的文章中,我描述了如何使用自定义类型 , 参数对象和构建器来 ...

  8. java 方法体 out 参数_Java方法详解

    方法详解 1.方法的定义 Java方法是语句的集合,它们在一起执行一个功能 方法是解决一类问题的步骤的有序组合,包含于类或对象中:在程序中被创建,在其它地方被引用 设计方法的原则:要遵守原子性,即一个 ...

  9. java 方法 示例_Java方法参考类型和示例

    java 方法 示例 Java Method Reference was introduced in Java 8, along with lambda expressions. The method ...

  10. JAVA中dot的用法_Java 方法

    方法(有的人喜欢叫函数)是一段可重用的代码段. 一.方法的使用 1. 方法定义 方法定义的语法形式: [修饰符] 返回值类型 方法名([参数类型 参数名]){ ... 方法体 ...return返回值 ...

最新文章

  1. python50种算法_收藏 | 一文洞悉Python必备50种算法(附解析)
  2. java 10新_【Java基础】Java10 新特性
  3. CUDA(六). 从并行排序方法理解并行化思维——冒泡、归并、双调排序的GPU实现
  4. Machine Learning week 1 quiz: Linear Algebra
  5. 程序员应该遵守的编程原则
  6. leetcode474. 一和零(动态规划)
  7. 大神干货:冠军选手分享解题思路,助你轻松突围初赛
  8. 软考高级报考,科目思维导图概览和介绍
  9. 《数学分析新讲》_张筑生,12.5节:隐函数定理(1)
  10. 用Python做一个游戏辅助脚本,完整编程思路分享!
  11. cad线性标注命令_CAD线性标注命令的使用方法
  12. 滴滴资深分析专家:数据如何驱动业务增长
  13. [N1CTF 2022] solve_pow,baby_N1ES
  14. 证券行业智慧化转型思考
  15. 服务器系统关机了怎么办,各种服务器系统的关机
  16. gcc ld链接脚语法简明讲解
  17. 专家建议加速2G3G退网、5G取代4G,你感受到网速快了吗?
  18. HTML基础,CSS基础
  19. 程序员上了年纪可以做啥?
  20. 打造完美用户体验:“天翼平台开放日”沙龙纪实(转载)

热门文章

  1. java对菜单项的监听_我是新手,请问大神java菜单项和下拉列表添加监听和监听方法???有变量和方法就行了...
  2. java中getlast_Java ArrayDeque getLast()用法及代码示例
  3. mac下nvm_【干货分享】Mac最全用法指导,学不会学费全退!!!(反正我也没收你们学费)...
  4. VMware安装Linux(CentOS7)
  5. opencv for andriod java代码实现霍夫变化(HoughLinesP)
  6. 学习easyui疑问(二)
  7. ArcEngine和GDAL读写栅格数据机制对比(一)
  8. c#namespace
  9. es 创建索引_从一道面试题来看ES的分布式架构原理
  10. MySQL 表分区 Partition