深入理解 Java String#intern() 内存模型

字符串常量池是一个固定大小的HashMap,桶的数量默认是1009,
从Java7u40开始,该默认值增大到60013。在Java6当中,字符串常量池是放在Perm空间的,从Java7开始,字符串常量池被移到Heap空间。下面,我们通过测试程序来窥探字符串常量池在Java6,Java7两个不同版本底下的内存分配情况。

测试程序


  1. public class StringPoolTest { 
  2.  
  3.     public void testStringPoolWithLongString(){ 
  4.         long i=0; 
  5.         while(true){ 
  6.             String longString = "This is a very long string, very very long string to test the gc behavior of the string constant pool"+i; 
  7.             longString.intern(); 
  8.             i++; 
  9.         } 
  10.     } 
  11.  
  12.     public static void main(String[] args){ 
  13.         StringPoolTest stringPoolTest = new StringPoolTest(); 
  14.         stringPoolTest.testStringPoolWithLongString(); 
  15.     } 

测试程序很简单,一个死循环,循环里面通过递增变量i制造唯一的字符串,然后用main函数启动程序。

Java 6

我们使用版本Jdk1.6.0_29来跑该程序,打开Java VisualVM监控,可以看到,Perm区不断发生GC,由此的出结论,虽然字符串常量池放在Perm空间,但当Perm空间接近满的时候,JVM会将字符串常量池中的无用字符串回收掉。

Java 7

下面,我们切换到Jdk1.7.0_67重跑该程序,可以看到Perm区内存分配曲线很平滑,没有出现内存分配的现象。

但在Heap空间,新的对象不断产生,然后不断触发GC

结论

由于Perm区大小是有限的,通常只有几十MB,所以不推荐在Java6下广泛使用String.intern(),这篇文章string-intern-in-java-6-7-8的性能测试表明,在Java6底下大量使用intern()会导致应用性能的显著下降,还有可能产生OOM错误。但从Java7开始,字符串常量池被移到了Heap空间,Heap空间的大小只受制于机器的真实内存大小,因此,在Java7下使用String.intern()能更有效地减少重复String对象对内存的占用。

来源:51CTO

时间: 2025-01-02 06:14:35

深入理解 Java String#intern() 内存模型的相关文章

Java String#intern() 内存模型详解教程

大家知道,Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回. 字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, 从Java7u40开始,该默认值增大到60013.在Java6当中,字符串常量池是放在Perm空间的,从Java7开始,字符串常量池被移到 Heap空间.下面,我们通过测试程序来窥探字符串常量池在Java6,Java7两个不同版本底下的内存分配情况. 测试程序  代码如

Java内存模型-jsr133规范介绍(转)

最近在看<深入理解Java虚拟机:JVM高级特性与最佳实践>讲到了线程相关的细节知识,里面讲述了关于java内存模型,也就是jsr 133定义的规范. 系统的看了jsr 133规范的前面几个章节的内容,觉得受益匪浅.废话不说,简要的介绍一下java内存规范. 什么是内存规范 在jsr-133中是这么定义的 A memory model describes, given a program and an execution trace of that program, whether the e

java String的intern方法_java

首先我们应该清楚的是JDK1.6和JDK1.7中String类的intern方法还是有差别的:   JDK1.6中的intern:    调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中这个String值的引用:如果不存在的话,则会将原先堆中的该字符串拷贝一份到常量池中.   JDK1.7中的intern:    调用intern方法的时候首先会去常量池中查看是否存在与当前String值相同的值,如果存在的话,则直接返回常量池中

《深入理解Java虚拟机》读书笔记

背景 并发处理的广泛应用是使得Amdahl定律代替摩尔定律成为计算机性能发展的源动力的根本原因,也是人类压榨计算机运算能力最有力的武器 Amdahl 定律通过系统中的并行化与串行化的比重来描述多处理器系统能获得的运算加速能力. 摩尔定律则用于描述处理器晶体管数量与运行效率之间的发展关系. 这两个定律的更替代表了近年来硬件发展从追求处理器频率到追求多核心并行处理的发展过程. 高效并发 物理机上的并发解决方案 在当前这个多核处理器时代,"让计算机并发执行若干个运算任务"和"更充分

JVM学习笔记(一)——内存模型

对于Java程序员来说,他们相比C++程序员最幸福的一点就是不用自己管理内存,内存的分配和回收都由虚拟机完成.然而,正是由于该原因,一旦虚拟机内存管理出现问题,比如出现内存泄漏或溢出,排查起来将是非常困难的.所以尽管不用亲自动手管理内存,但是了解虚拟机的内存管理机制还是很有必要的. 运行时数据区域 首先来看下Java虚拟机在运行时的数据区域划分,Java虚拟机在Java程序运行时会将内存区域划分成若干个不同的区域,各自负责不同的职责. 1.1 程序计数器 程序计数器(PC)的概念比较简单,记录当

深入理解Java内存模型系列篇

[本文转载于深入理解Java内存模型,可点击每个章节标题查看原文] 深入理解Java内存模型(一)--基础 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线程之间必

深入理解Java内存模型(六) final

与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问.对于final域,编译 器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操 作之间不能重排序. 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序. 下面,我们通过一些示例性的代码来分别说明这两个规则: public class FinalExample { int i; //普通变量 fin

深入理解Java内存模型(五) 锁

锁的释放-获取建立的happens before 关系 锁是java并发编程中最重要的同步机制.锁除了让 临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 下面是锁释放-获取 的示例代码: class MonitorExample { int a = 0; public synchronized void writer() { //1 a++; //2 } //3 public synchronized void reader() { //4 int i = a; //5 -

深入理解Java内存模型(四) volatile

volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解 volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个 读/写操作做了同步.下面我们通过具体的示例来说明,请看下面的示例代码: class VolatileFeaturesExample { volatile long vl = 0L; //使用volatile声明64位的long型变量 public void set(long l) { vl